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 #include <google/protobuf/util/internal/protostream_objectsource.h>
32
33 #include <utility>
34
35 #include <google/protobuf/stubs/casts.h>
36 #include <google/protobuf/stubs/logging.h>
37 #include <google/protobuf/stubs/common.h>
38 #include <google/protobuf/stubs/stringprintf.h>
39 #include <google/protobuf/stubs/time.h>
40 #include <google/protobuf/io/coded_stream.h>
41 #include <google/protobuf/io/zero_copy_stream_impl.h>
42 #include <google/protobuf/descriptor.h>
43 #include <google/protobuf/wire_format.h>
44 #include <google/protobuf/wire_format_lite.h>
45 #include <google/protobuf/util/internal/field_mask_utility.h>
46 #include <google/protobuf/util/internal/constants.h>
47 #include <google/protobuf/util/internal/utility.h>
48 #include <google/protobuf/stubs/strutil.h>
49 #include <google/protobuf/stubs/map_util.h>
50 #include <google/protobuf/stubs/status_macros.h>
51
52
53 namespace google {
54 namespace protobuf {
55 namespace util {
56 using util::Status;
57 using util::StatusOr;
58 namespace error {
59 using util::error::Code;
60 using util::error::INTERNAL;
61 }
62 namespace converter {
63
64 using google::protobuf::Descriptor;
65 using google::protobuf::EnumValueDescriptor;
66 using google::protobuf::FieldDescriptor;
67 using google::protobuf::internal::WireFormat;
68 using google::protobuf::internal::WireFormatLite;
69 using util::Status;
70 using util::StatusOr;
71
72 namespace {
73
74 static int kDefaultMaxRecursionDepth = 64;
75
76 // Finds a field with the given number. NULL if none found.
77 const google::protobuf::Field* FindFieldByNumber(
78 const google::protobuf::Type& type, int number);
79
80 // Returns true if the field is packable.
81 bool IsPackable(const google::protobuf::Field& field);
82
83 // Finds an enum value with the given number. NULL if none found.
84 const google::protobuf::EnumValue* FindEnumValueByNumber(
85 const google::protobuf::Enum& tech_enum, int number);
86
87 // Utility function to format nanos.
88 const string FormatNanos(uint32 nanos);
89
MapKeyDefaultValueAsString(const google::protobuf::Field & field)90 StatusOr<string> MapKeyDefaultValueAsString(
91 const google::protobuf::Field& field) {
92 switch (field.kind()) {
93 case google::protobuf::Field_Kind_TYPE_BOOL:
94 return string("false");
95 case google::protobuf::Field_Kind_TYPE_INT32:
96 case google::protobuf::Field_Kind_TYPE_INT64:
97 case google::protobuf::Field_Kind_TYPE_UINT32:
98 case google::protobuf::Field_Kind_TYPE_UINT64:
99 case google::protobuf::Field_Kind_TYPE_SINT32:
100 case google::protobuf::Field_Kind_TYPE_SINT64:
101 case google::protobuf::Field_Kind_TYPE_SFIXED32:
102 case google::protobuf::Field_Kind_TYPE_SFIXED64:
103 case google::protobuf::Field_Kind_TYPE_FIXED32:
104 case google::protobuf::Field_Kind_TYPE_FIXED64:
105 return string("0");
106 case google::protobuf::Field_Kind_TYPE_STRING:
107 return string();
108 default:
109 return Status(util::error::INTERNAL, "Invalid map key type.");
110 }
111 }
112 } // namespace
113
114
ProtoStreamObjectSource(google::protobuf::io::CodedInputStream * stream,TypeResolver * type_resolver,const google::protobuf::Type & type)115 ProtoStreamObjectSource::ProtoStreamObjectSource(
116 google::protobuf::io::CodedInputStream* stream, TypeResolver* type_resolver,
117 const google::protobuf::Type& type)
118 : stream_(stream),
119 typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
120 own_typeinfo_(true),
121 type_(type),
122 use_lower_camel_for_enums_(false),
123 recursion_depth_(0),
124 max_recursion_depth_(kDefaultMaxRecursionDepth) {
125 GOOGLE_LOG_IF(DFATAL, stream == NULL) << "Input stream is NULL.";
126 }
127
ProtoStreamObjectSource(google::protobuf::io::CodedInputStream * stream,const TypeInfo * typeinfo,const google::protobuf::Type & type)128 ProtoStreamObjectSource::ProtoStreamObjectSource(
129 google::protobuf::io::CodedInputStream* stream, const TypeInfo* typeinfo,
130 const google::protobuf::Type& type)
131 : stream_(stream),
132 typeinfo_(typeinfo),
133 own_typeinfo_(false),
134 type_(type),
135 use_lower_camel_for_enums_(false),
136 recursion_depth_(0),
137 max_recursion_depth_(kDefaultMaxRecursionDepth) {
138 GOOGLE_LOG_IF(DFATAL, stream == NULL) << "Input stream is NULL.";
139 }
140
~ProtoStreamObjectSource()141 ProtoStreamObjectSource::~ProtoStreamObjectSource() {
142 if (own_typeinfo_) {
143 delete typeinfo_;
144 }
145 }
146
NamedWriteTo(StringPiece name,ObjectWriter * ow) const147 Status ProtoStreamObjectSource::NamedWriteTo(StringPiece name,
148 ObjectWriter* ow) const {
149 return WriteMessage(type_, name, 0, true, ow);
150 }
151
FindAndVerifyField(const google::protobuf::Type & type,uint32 tag) const152 const google::protobuf::Field* ProtoStreamObjectSource::FindAndVerifyField(
153 const google::protobuf::Type& type, uint32 tag) const {
154 // Lookup the new field in the type by tag number.
155 const google::protobuf::Field* field = FindFieldByNumber(type, tag >> 3);
156 // Verify if the field corresponds to the wire type in tag.
157 // If there is any discrepancy, mark the field as not found.
158 if (field != NULL) {
159 WireFormatLite::WireType expected_type =
160 WireFormatLite::WireTypeForFieldType(
161 static_cast<WireFormatLite::FieldType>(field->kind()));
162 WireFormatLite::WireType actual_type = WireFormatLite::GetTagWireType(tag);
163 if (actual_type != expected_type &&
164 (!IsPackable(*field) ||
165 actual_type != WireFormatLite::WIRETYPE_LENGTH_DELIMITED)) {
166 field = NULL;
167 }
168 }
169 return field;
170 }
171
WriteMessage(const google::protobuf::Type & type,StringPiece name,const uint32 end_tag,bool include_start_and_end,ObjectWriter * ow) const172 Status ProtoStreamObjectSource::WriteMessage(const google::protobuf::Type& type,
173 StringPiece name,
174 const uint32 end_tag,
175 bool include_start_and_end,
176 ObjectWriter* ow) const {
177
178 const TypeRenderer* type_renderer = FindTypeRenderer(type.name());
179 if (type_renderer != NULL) {
180 return (*type_renderer)(this, type, name, ow);
181 }
182
183 const google::protobuf::Field* field = NULL;
184 string field_name;
185 // last_tag set to dummy value that is different from tag.
186 uint32 tag = stream_->ReadTag(), last_tag = tag + 1;
187
188 if (include_start_and_end) {
189 ow->StartObject(name);
190 }
191 while (tag != end_tag) {
192 if (tag != last_tag) { // Update field only if tag is changed.
193 last_tag = tag;
194 field = FindAndVerifyField(type, tag);
195 if (field != NULL) {
196 field_name = field->json_name();
197 }
198 }
199 if (field == NULL) {
200 // If we didn't find a field, skip this unknown tag.
201 // TODO(wpoon): Check return boolean value.
202 WireFormat::SkipField(stream_, tag, NULL);
203 tag = stream_->ReadTag();
204 continue;
205 }
206
207 if (field->cardinality() ==
208 google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
209 bool check_maps = true;
210
211 if (check_maps && IsMap(*field)) {
212 ow->StartObject(field_name);
213 ASSIGN_OR_RETURN(tag, RenderMap(field, field_name, tag, ow));
214 ow->EndObject();
215 } else {
216 ASSIGN_OR_RETURN(tag, RenderList(field, field_name, tag, ow));
217 }
218 } else {
219 // Render the field.
220 RETURN_IF_ERROR(RenderField(field, field_name, ow));
221 tag = stream_->ReadTag();
222 }
223 }
224 if (include_start_and_end) {
225 ow->EndObject();
226 }
227 return Status::OK;
228 }
229
RenderList(const google::protobuf::Field * field,StringPiece name,uint32 list_tag,ObjectWriter * ow) const230 StatusOr<uint32> ProtoStreamObjectSource::RenderList(
231 const google::protobuf::Field* field, StringPiece name, uint32 list_tag,
232 ObjectWriter* ow) const {
233 uint32 tag_to_return = 0;
234 ow->StartList(name);
235 if (IsPackable(*field) &&
236 list_tag ==
237 WireFormatLite::MakeTag(field->number(),
238 WireFormatLite::WIRETYPE_LENGTH_DELIMITED)) {
239 RETURN_IF_ERROR(RenderPacked(field, ow));
240 // Since packed fields have a single tag, read another tag from stream to
241 // return.
242 tag_to_return = stream_->ReadTag();
243 } else {
244 do {
245 RETURN_IF_ERROR(RenderField(field, "", ow));
246 } while ((tag_to_return = stream_->ReadTag()) == list_tag);
247 }
248 ow->EndList();
249 return tag_to_return;
250 }
251
RenderMap(const google::protobuf::Field * field,StringPiece name,uint32 list_tag,ObjectWriter * ow) const252 StatusOr<uint32> ProtoStreamObjectSource::RenderMap(
253 const google::protobuf::Field* field, StringPiece name, uint32 list_tag,
254 ObjectWriter* ow) const {
255 const google::protobuf::Type* field_type =
256 typeinfo_->GetTypeByTypeUrl(field->type_url());
257 uint32 tag_to_return = 0;
258 do {
259 // Render map entry message type.
260 uint32 buffer32;
261 stream_->ReadVarint32(&buffer32); // message length
262 int old_limit = stream_->PushLimit(buffer32);
263 string map_key;
264 for (uint32 tag = stream_->ReadTag(); tag != 0; tag = stream_->ReadTag()) {
265 const google::protobuf::Field* field =
266 FindAndVerifyField(*field_type, tag);
267 if (field == NULL) {
268 WireFormat::SkipField(stream_, tag, NULL);
269 continue;
270 }
271 // Map field numbers are key = 1 and value = 2
272 if (field->number() == 1) {
273 map_key = ReadFieldValueAsString(*field);
274 } else if (field->number() == 2) {
275 if (map_key.empty()) {
276 // An absent map key is treated as the default.
277 const google::protobuf::Field* key_field =
278 FindFieldByNumber(*field_type, 1);
279 if (key_field == NULL) {
280 // The Type info for this map entry is incorrect. It should always
281 // have a field named "key" and with field number 1.
282 return Status(util::error::INTERNAL, "Invalid map entry.");
283 }
284 ASSIGN_OR_RETURN(map_key, MapKeyDefaultValueAsString(*key_field));
285 }
286 RETURN_IF_ERROR(RenderField(field, map_key, ow));
287 } else {
288 // The Type info for this map entry is incorrect. It should contain
289 // exactly two fields with field number 1 and 2.
290 return Status(util::error::INTERNAL, "Invalid map entry.");
291 }
292 }
293 stream_->PopLimit(old_limit);
294 } while ((tag_to_return = stream_->ReadTag()) == list_tag);
295 return tag_to_return;
296 }
297
RenderPacked(const google::protobuf::Field * field,ObjectWriter * ow) const298 Status ProtoStreamObjectSource::RenderPacked(
299 const google::protobuf::Field* field, ObjectWriter* ow) const {
300 uint32 length;
301 stream_->ReadVarint32(&length);
302 int old_limit = stream_->PushLimit(length);
303 while (stream_->BytesUntilLimit() > 0) {
304 RETURN_IF_ERROR(RenderField(field, StringPiece(), ow));
305 }
306 stream_->PopLimit(old_limit);
307 return Status::OK;
308 }
309
RenderTimestamp(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)310 Status ProtoStreamObjectSource::RenderTimestamp(
311 const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
312 StringPiece field_name, ObjectWriter* ow) {
313 pair<int64, int32> p = os->ReadSecondsAndNanos(type);
314 int64 seconds = p.first;
315 int32 nanos = p.second;
316 if (seconds > kTimestampMaxSeconds || seconds < kTimestampMinSeconds) {
317 return Status(
318 util::error::INTERNAL,
319 StrCat("Timestamp seconds exceeds limit for field: ", field_name));
320 }
321
322 if (nanos < 0 || nanos >= kNanosPerSecond) {
323 return Status(
324 util::error::INTERNAL,
325 StrCat("Timestamp nanos exceeds limit for field: ", field_name));
326 }
327
328 ow->RenderString(field_name,
329 ::google::protobuf::internal::FormatTime(seconds, nanos));
330
331 return Status::OK;
332 }
333
RenderDuration(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)334 Status ProtoStreamObjectSource::RenderDuration(
335 const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
336 StringPiece field_name, ObjectWriter* ow) {
337 pair<int64, int32> p = os->ReadSecondsAndNanos(type);
338 int64 seconds = p.first;
339 int32 nanos = p.second;
340 if (seconds > kDurationMaxSeconds || seconds < kDurationMinSeconds) {
341 return Status(
342 util::error::INTERNAL,
343 StrCat("Duration seconds exceeds limit for field: ", field_name));
344 }
345
346 if (nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) {
347 return Status(
348 util::error::INTERNAL,
349 StrCat("Duration nanos exceeds limit for field: ", field_name));
350 }
351
352 string sign = "";
353 if (seconds < 0) {
354 if (nanos > 0) {
355 return Status(util::error::INTERNAL,
356 StrCat("Duration nanos is non-negative, but seconds is "
357 "negative for field: ",
358 field_name));
359 }
360 sign = "-";
361 seconds = -seconds;
362 nanos = -nanos;
363 } else if (seconds == 0 && nanos < 0) {
364 sign = "-";
365 nanos = -nanos;
366 }
367 string formatted_duration = StringPrintf("%s%lld%ss", sign.c_str(), seconds,
368 FormatNanos(nanos).c_str());
369 ow->RenderString(field_name, formatted_duration);
370 return Status::OK;
371 }
372
RenderDouble(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)373 Status ProtoStreamObjectSource::RenderDouble(const ProtoStreamObjectSource* os,
374 const google::protobuf::Type& type,
375 StringPiece field_name,
376 ObjectWriter* ow) {
377 uint32 tag = os->stream_->ReadTag();
378 uint64 buffer64 = 0; // default value of Double wrapper value
379 if (tag != 0) {
380 os->stream_->ReadLittleEndian64(&buffer64);
381 os->stream_->ReadTag();
382 }
383 ow->RenderDouble(field_name, bit_cast<double>(buffer64));
384 return Status::OK;
385 }
386
RenderFloat(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)387 Status ProtoStreamObjectSource::RenderFloat(const ProtoStreamObjectSource* os,
388 const google::protobuf::Type& type,
389 StringPiece field_name,
390 ObjectWriter* ow) {
391 uint32 tag = os->stream_->ReadTag();
392 uint32 buffer32 = 0; // default value of Float wrapper value
393 if (tag != 0) {
394 os->stream_->ReadLittleEndian32(&buffer32);
395 os->stream_->ReadTag();
396 }
397 ow->RenderFloat(field_name, bit_cast<float>(buffer32));
398 return Status::OK;
399 }
400
RenderInt64(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)401 Status ProtoStreamObjectSource::RenderInt64(const ProtoStreamObjectSource* os,
402 const google::protobuf::Type& type,
403 StringPiece field_name,
404 ObjectWriter* ow) {
405 uint32 tag = os->stream_->ReadTag();
406 uint64 buffer64 = 0; // default value of Int64 wrapper value
407 if (tag != 0) {
408 os->stream_->ReadVarint64(&buffer64);
409 os->stream_->ReadTag();
410 }
411 ow->RenderInt64(field_name, bit_cast<int64>(buffer64));
412 return Status::OK;
413 }
414
RenderUInt64(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)415 Status ProtoStreamObjectSource::RenderUInt64(const ProtoStreamObjectSource* os,
416 const google::protobuf::Type& type,
417 StringPiece field_name,
418 ObjectWriter* ow) {
419 uint32 tag = os->stream_->ReadTag();
420 uint64 buffer64 = 0; // default value of UInt64 wrapper value
421 if (tag != 0) {
422 os->stream_->ReadVarint64(&buffer64);
423 os->stream_->ReadTag();
424 }
425 ow->RenderUint64(field_name, bit_cast<uint64>(buffer64));
426 return Status::OK;
427 }
428
RenderInt32(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)429 Status ProtoStreamObjectSource::RenderInt32(const ProtoStreamObjectSource* os,
430 const google::protobuf::Type& type,
431 StringPiece field_name,
432 ObjectWriter* ow) {
433 uint32 tag = os->stream_->ReadTag();
434 uint32 buffer32 = 0; // default value of Int32 wrapper value
435 if (tag != 0) {
436 os->stream_->ReadVarint32(&buffer32);
437 os->stream_->ReadTag();
438 }
439 ow->RenderInt32(field_name, bit_cast<int32>(buffer32));
440 return Status::OK;
441 }
442
RenderUInt32(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)443 Status ProtoStreamObjectSource::RenderUInt32(const ProtoStreamObjectSource* os,
444 const google::protobuf::Type& type,
445 StringPiece field_name,
446 ObjectWriter* ow) {
447 uint32 tag = os->stream_->ReadTag();
448 uint32 buffer32 = 0; // default value of UInt32 wrapper value
449 if (tag != 0) {
450 os->stream_->ReadVarint32(&buffer32);
451 os->stream_->ReadTag();
452 }
453 ow->RenderUint32(field_name, bit_cast<uint32>(buffer32));
454 return Status::OK;
455 }
456
RenderBool(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)457 Status ProtoStreamObjectSource::RenderBool(const ProtoStreamObjectSource* os,
458 const google::protobuf::Type& type,
459 StringPiece field_name,
460 ObjectWriter* ow) {
461 uint32 tag = os->stream_->ReadTag();
462 uint64 buffer64 = 0; // results in 'false' value as default, which is the
463 // default value of Bool wrapper
464 if (tag != 0) {
465 os->stream_->ReadVarint64(&buffer64);
466 os->stream_->ReadTag();
467 }
468 ow->RenderBool(field_name, buffer64 != 0);
469 return Status::OK;
470 }
471
RenderString(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)472 Status ProtoStreamObjectSource::RenderString(const ProtoStreamObjectSource* os,
473 const google::protobuf::Type& type,
474 StringPiece field_name,
475 ObjectWriter* ow) {
476 uint32 tag = os->stream_->ReadTag();
477 uint32 buffer32;
478 string str; // default value of empty for String wrapper
479 if (tag != 0) {
480 os->stream_->ReadVarint32(&buffer32); // string size.
481 os->stream_->ReadString(&str, buffer32);
482 os->stream_->ReadTag();
483 }
484 ow->RenderString(field_name, str);
485 return Status::OK;
486 }
487
RenderBytes(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)488 Status ProtoStreamObjectSource::RenderBytes(const ProtoStreamObjectSource* os,
489 const google::protobuf::Type& type,
490 StringPiece field_name,
491 ObjectWriter* ow) {
492 uint32 tag = os->stream_->ReadTag();
493 uint32 buffer32;
494 string str;
495 if (tag != 0) {
496 os->stream_->ReadVarint32(&buffer32);
497 os->stream_->ReadString(&str, buffer32);
498 os->stream_->ReadTag();
499 }
500 ow->RenderBytes(field_name, str);
501 return Status::OK;
502 }
503
RenderStruct(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)504 Status ProtoStreamObjectSource::RenderStruct(const ProtoStreamObjectSource* os,
505 const google::protobuf::Type& type,
506 StringPiece field_name,
507 ObjectWriter* ow) {
508 const google::protobuf::Field* field = NULL;
509 uint32 tag = os->stream_->ReadTag();
510 ow->StartObject(field_name);
511 while (tag != 0) {
512 field = os->FindAndVerifyField(type, tag);
513 // google.protobuf.Struct has only one field that is a map. Hence we use
514 // RenderMap to render that field.
515 if (os->IsMap(*field)) {
516 ASSIGN_OR_RETURN(tag, os->RenderMap(field, field_name, tag, ow));
517 }
518 }
519 ow->EndObject();
520 return Status::OK;
521 }
522
RenderStructValue(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)523 Status ProtoStreamObjectSource::RenderStructValue(
524 const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
525 StringPiece field_name, ObjectWriter* ow) {
526 const google::protobuf::Field* field = NULL;
527 for (uint32 tag = os->stream_->ReadTag(); tag != 0;
528 tag = os->stream_->ReadTag()) {
529 field = os->FindAndVerifyField(type, tag);
530 if (field == NULL) {
531 WireFormat::SkipField(os->stream_, tag, NULL);
532 continue;
533 }
534 RETURN_IF_ERROR(os->RenderField(field, field_name, ow));
535 }
536 return Status::OK;
537 }
538
539 // TODO(skarvaje): Avoid code duplication of for loops and SkipField logic.
RenderStructListValue(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)540 Status ProtoStreamObjectSource::RenderStructListValue(
541 const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
542 StringPiece field_name, ObjectWriter* ow) {
543 uint32 tag = os->stream_->ReadTag();
544
545 // Render empty list when we find empty ListValue message.
546 if (tag == 0) {
547 ow->StartList(field_name);
548 ow->EndList();
549 return Status::OK;
550 }
551
552 while (tag != 0) {
553 const google::protobuf::Field* field = os->FindAndVerifyField(type, tag);
554 if (field == NULL) {
555 WireFormat::SkipField(os->stream_, tag, NULL);
556 tag = os->stream_->ReadTag();
557 continue;
558 }
559 ASSIGN_OR_RETURN(tag, os->RenderList(field, field_name, tag, ow));
560 }
561 return Status::OK;
562 }
563
RenderAny(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)564 Status ProtoStreamObjectSource::RenderAny(const ProtoStreamObjectSource* os,
565 const google::protobuf::Type& type,
566 StringPiece field_name,
567 ObjectWriter* ow) {
568 // An Any is of the form { string type_url = 1; bytes value = 2; }
569 uint32 tag;
570 string type_url;
571 string value;
572
573 // First read out the type_url and value from the proto stream
574 for (tag = os->stream_->ReadTag(); tag != 0; tag = os->stream_->ReadTag()) {
575 const google::protobuf::Field* field = os->FindAndVerifyField(type, tag);
576 if (field == NULL) {
577 WireFormat::SkipField(os->stream_, tag, NULL);
578 continue;
579 }
580 // 'type_url' has field number of 1 and 'value' has field number 2
581 // //google/protobuf/any.proto
582 if (field->number() == 1) {
583 // read type_url
584 uint32 type_url_size;
585 os->stream_->ReadVarint32(&type_url_size);
586 os->stream_->ReadString(&type_url, type_url_size);
587 } else if (field->number() == 2) {
588 // read value
589 uint32 value_size;
590 os->stream_->ReadVarint32(&value_size);
591 os->stream_->ReadString(&value, value_size);
592 }
593 }
594
595 // If there is no value, we don't lookup the type, we just output it (if
596 // present). If both type and value are empty we output an empty object.
597 if (value.empty()) {
598 ow->StartObject(field_name);
599 if (!type_url.empty()) {
600 ow->RenderString("@type", type_url);
601 }
602 ow->EndObject();
603 return util::Status::OK;
604 }
605
606 // If there is a value but no type, we cannot render it, so report an error.
607 if (type_url.empty()) {
608 // TODO(sven): Add an external message once those are ready.
609 return util::Status(util::error::INTERNAL,
610 "Invalid Any, the type_url is missing.");
611 }
612
613 util::StatusOr<const google::protobuf::Type*> resolved_type =
614 os->typeinfo_->ResolveTypeUrl(type_url);
615
616 if (!resolved_type.ok()) {
617 // Convert into an internal error, since this means the backend gave us
618 // an invalid response (missing or invalid type information).
619 return util::Status(util::error::INTERNAL,
620 resolved_type.status().error_message());
621 }
622 // nested_type cannot be null at this time.
623 const google::protobuf::Type* nested_type = resolved_type.ValueOrDie();
624
625 google::protobuf::io::ArrayInputStream zero_copy_stream(value.data(), value.size());
626 google::protobuf::io::CodedInputStream in_stream(&zero_copy_stream);
627 // We know the type so we can render it. Recursively parse the nested stream
628 // using a nested ProtoStreamObjectSource using our nested type information.
629 ProtoStreamObjectSource nested_os(&in_stream, os->typeinfo_, *nested_type);
630
631 // We manually call start and end object here so we can inject the @type.
632 ow->StartObject(field_name);
633 ow->RenderString("@type", type_url);
634 util::Status result =
635 nested_os.WriteMessage(nested_os.type_, "value", 0, false, ow);
636 ow->EndObject();
637 return result;
638 }
639
RenderFieldMask(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)640 Status ProtoStreamObjectSource::RenderFieldMask(
641 const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
642 StringPiece field_name, ObjectWriter* ow) {
643 string combined;
644 uint32 buffer32;
645 uint32 paths_field_tag = 0;
646 for (uint32 tag = os->stream_->ReadTag(); tag != 0;
647 tag = os->stream_->ReadTag()) {
648 if (paths_field_tag == 0) {
649 const google::protobuf::Field* field = os->FindAndVerifyField(type, tag);
650 if (field != NULL && field->number() == 1 &&
651 field->name() == "paths") {
652 paths_field_tag = tag;
653 }
654 }
655 if (paths_field_tag != tag) {
656 return util::Status(util::error::INTERNAL,
657 "Invalid FieldMask, unexpected field.");
658 }
659 string str;
660 os->stream_->ReadVarint32(&buffer32); // string size.
661 os->stream_->ReadString(&str, buffer32);
662 if (!combined.empty()) {
663 combined.append(",");
664 }
665 combined.append(ConvertFieldMaskPath(str, &ToCamelCase));
666 }
667 ow->RenderString(field_name, combined);
668 return Status::OK;
669 }
670
671
672 hash_map<string, ProtoStreamObjectSource::TypeRenderer>*
673 ProtoStreamObjectSource::renderers_ = NULL;
674 GOOGLE_PROTOBUF_DECLARE_ONCE(source_renderers_init_);
675
InitRendererMap()676 void ProtoStreamObjectSource::InitRendererMap() {
677 renderers_ = new hash_map<string, ProtoStreamObjectSource::TypeRenderer>();
678 (*renderers_)["google.protobuf.Timestamp"] =
679 &ProtoStreamObjectSource::RenderTimestamp;
680 (*renderers_)["google.protobuf.Duration"] =
681 &ProtoStreamObjectSource::RenderDuration;
682 (*renderers_)["google.protobuf.DoubleValue"] =
683 &ProtoStreamObjectSource::RenderDouble;
684 (*renderers_)["google.protobuf.FloatValue"] =
685 &ProtoStreamObjectSource::RenderFloat;
686 (*renderers_)["google.protobuf.Int64Value"] =
687 &ProtoStreamObjectSource::RenderInt64;
688 (*renderers_)["google.protobuf.UInt64Value"] =
689 &ProtoStreamObjectSource::RenderUInt64;
690 (*renderers_)["google.protobuf.Int32Value"] =
691 &ProtoStreamObjectSource::RenderInt32;
692 (*renderers_)["google.protobuf.UInt32Value"] =
693 &ProtoStreamObjectSource::RenderUInt32;
694 (*renderers_)["google.protobuf.BoolValue"] =
695 &ProtoStreamObjectSource::RenderBool;
696 (*renderers_)["google.protobuf.StringValue"] =
697 &ProtoStreamObjectSource::RenderString;
698 (*renderers_)["google.protobuf.BytesValue"] =
699 &ProtoStreamObjectSource::RenderBytes;
700 (*renderers_)["google.protobuf.Any"] = &ProtoStreamObjectSource::RenderAny;
701 (*renderers_)["google.protobuf.Struct"] =
702 &ProtoStreamObjectSource::RenderStruct;
703 (*renderers_)["google.protobuf.Value"] =
704 &ProtoStreamObjectSource::RenderStructValue;
705 (*renderers_)["google.protobuf.ListValue"] =
706 &ProtoStreamObjectSource::RenderStructListValue;
707 (*renderers_)["google.protobuf.FieldMask"] =
708 &ProtoStreamObjectSource::RenderFieldMask;
709 ::google::protobuf::internal::OnShutdown(&DeleteRendererMap);
710 }
711
DeleteRendererMap()712 void ProtoStreamObjectSource::DeleteRendererMap() {
713 delete ProtoStreamObjectSource::renderers_;
714 renderers_ = NULL;
715 }
716
717 // static
718 ProtoStreamObjectSource::TypeRenderer*
FindTypeRenderer(const string & type_url)719 ProtoStreamObjectSource::FindTypeRenderer(const string& type_url) {
720 ::google::protobuf::GoogleOnceInit(&source_renderers_init_, &InitRendererMap);
721 return FindOrNull(*renderers_, type_url);
722 }
723
RenderField(const google::protobuf::Field * field,StringPiece field_name,ObjectWriter * ow) const724 Status ProtoStreamObjectSource::RenderField(
725 const google::protobuf::Field* field, StringPiece field_name,
726 ObjectWriter* ow) const {
727 // Short-circuit message types as it tends to call WriteMessage recursively
728 // and ends up using a lot of stack space. Keep the stack usage of this
729 // message small in order to preserve stack space and not crash.
730 if (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) {
731 uint32 buffer32;
732 stream_->ReadVarint32(&buffer32); // message length
733 int old_limit = stream_->PushLimit(buffer32);
734 // Get the nested message type for this field.
735 const google::protobuf::Type* type =
736 typeinfo_->GetTypeByTypeUrl(field->type_url());
737 if (type == NULL) {
738 return Status(util::error::INTERNAL,
739 StrCat("Invalid configuration. Could not find the type: ",
740 field->type_url()));
741 }
742
743 // Short-circuit any special type rendering to save call-stack space.
744 const TypeRenderer* type_renderer = FindTypeRenderer(type->name());
745
746 bool use_type_renderer = type_renderer != NULL;
747
748 if (use_type_renderer) {
749 RETURN_IF_ERROR((*type_renderer)(this, *type, field_name, ow));
750 } else {
751 RETURN_IF_ERROR(IncrementRecursionDepth(type->name(), field_name));
752 RETURN_IF_ERROR(WriteMessage(*type, field_name, 0, true, ow));
753 --recursion_depth_;
754 }
755 if (!stream_->ConsumedEntireMessage()) {
756 return Status(util::error::INVALID_ARGUMENT,
757 "Nested protocol message not parsed in its entirety.");
758 }
759 stream_->PopLimit(old_limit);
760 } else {
761 // Render all other non-message types.
762 return RenderNonMessageField(field, field_name, ow);
763 }
764 return Status::OK;
765 }
766
RenderNonMessageField(const google::protobuf::Field * field,StringPiece field_name,ObjectWriter * ow) const767 Status ProtoStreamObjectSource::RenderNonMessageField(
768 const google::protobuf::Field* field, StringPiece field_name,
769 ObjectWriter* ow) const {
770 // Temporary buffers of different types.
771 uint32 buffer32;
772 uint64 buffer64;
773 string strbuffer;
774 switch (field->kind()) {
775 case google::protobuf::Field_Kind_TYPE_BOOL: {
776 stream_->ReadVarint64(&buffer64);
777 ow->RenderBool(field_name, buffer64 != 0);
778 break;
779 }
780 case google::protobuf::Field_Kind_TYPE_INT32: {
781 stream_->ReadVarint32(&buffer32);
782 ow->RenderInt32(field_name, bit_cast<int32>(buffer32));
783 break;
784 }
785 case google::protobuf::Field_Kind_TYPE_INT64: {
786 stream_->ReadVarint64(&buffer64);
787 ow->RenderInt64(field_name, bit_cast<int64>(buffer64));
788 break;
789 }
790 case google::protobuf::Field_Kind_TYPE_UINT32: {
791 stream_->ReadVarint32(&buffer32);
792 ow->RenderUint32(field_name, bit_cast<uint32>(buffer32));
793 break;
794 }
795 case google::protobuf::Field_Kind_TYPE_UINT64: {
796 stream_->ReadVarint64(&buffer64);
797 ow->RenderUint64(field_name, bit_cast<uint64>(buffer64));
798 break;
799 }
800 case google::protobuf::Field_Kind_TYPE_SINT32: {
801 stream_->ReadVarint32(&buffer32);
802 ow->RenderInt32(field_name, WireFormatLite::ZigZagDecode32(buffer32));
803 break;
804 }
805 case google::protobuf::Field_Kind_TYPE_SINT64: {
806 stream_->ReadVarint64(&buffer64);
807 ow->RenderInt64(field_name, WireFormatLite::ZigZagDecode64(buffer64));
808 break;
809 }
810 case google::protobuf::Field_Kind_TYPE_SFIXED32: {
811 stream_->ReadLittleEndian32(&buffer32);
812 ow->RenderInt32(field_name, bit_cast<int32>(buffer32));
813 break;
814 }
815 case google::protobuf::Field_Kind_TYPE_SFIXED64: {
816 stream_->ReadLittleEndian64(&buffer64);
817 ow->RenderInt64(field_name, bit_cast<int64>(buffer64));
818 break;
819 }
820 case google::protobuf::Field_Kind_TYPE_FIXED32: {
821 stream_->ReadLittleEndian32(&buffer32);
822 ow->RenderUint32(field_name, bit_cast<uint32>(buffer32));
823 break;
824 }
825 case google::protobuf::Field_Kind_TYPE_FIXED64: {
826 stream_->ReadLittleEndian64(&buffer64);
827 ow->RenderUint64(field_name, bit_cast<uint64>(buffer64));
828 break;
829 }
830 case google::protobuf::Field_Kind_TYPE_FLOAT: {
831 stream_->ReadLittleEndian32(&buffer32);
832 ow->RenderFloat(field_name, bit_cast<float>(buffer32));
833 break;
834 }
835 case google::protobuf::Field_Kind_TYPE_DOUBLE: {
836 stream_->ReadLittleEndian64(&buffer64);
837 ow->RenderDouble(field_name, bit_cast<double>(buffer64));
838 break;
839 }
840 case google::protobuf::Field_Kind_TYPE_ENUM: {
841 stream_->ReadVarint32(&buffer32);
842
843 // If the field represents an explicit NULL value, render null.
844 if (field->type_url() == kStructNullValueTypeUrl) {
845 ow->RenderNull(field_name);
846 break;
847 }
848
849 // Get the nested enum type for this field.
850 // TODO(skarvaje): Avoid string manipulation. Find ways to speed this
851 // up.
852 const google::protobuf::Enum* en =
853 typeinfo_->GetEnumByTypeUrl(field->type_url());
854 // Lookup the name of the enum, and render that. Skips unknown enums.
855 if (en != NULL) {
856 const google::protobuf::EnumValue* enum_value =
857 FindEnumValueByNumber(*en, buffer32);
858 if (enum_value != NULL) {
859 if (use_lower_camel_for_enums_)
860 ow->RenderString(field_name, ToCamelCase(enum_value->name()));
861 else
862 ow->RenderString(field_name, enum_value->name());
863 }
864 } else {
865 GOOGLE_LOG(INFO) << "Unknown enum skipped: " << field->type_url();
866 }
867 break;
868 }
869 case google::protobuf::Field_Kind_TYPE_STRING: {
870 stream_->ReadVarint32(&buffer32); // string size.
871 stream_->ReadString(&strbuffer, buffer32);
872 ow->RenderString(field_name, strbuffer);
873 break;
874 }
875 case google::protobuf::Field_Kind_TYPE_BYTES: {
876 stream_->ReadVarint32(&buffer32); // bytes size.
877 stream_->ReadString(&strbuffer, buffer32);
878 ow->RenderBytes(field_name, strbuffer);
879 break;
880 }
881 default:
882 break;
883 }
884 return Status::OK;
885 }
886
887 // TODO(skarvaje): Fix this to avoid code duplication.
ReadFieldValueAsString(const google::protobuf::Field & field) const888 const string ProtoStreamObjectSource::ReadFieldValueAsString(
889 const google::protobuf::Field& field) const {
890 string result;
891 switch (field.kind()) {
892 case google::protobuf::Field_Kind_TYPE_BOOL: {
893 uint64 buffer64;
894 stream_->ReadVarint64(&buffer64);
895 result = buffer64 != 0 ? "true" : "false";
896 break;
897 }
898 case google::protobuf::Field_Kind_TYPE_INT32: {
899 uint32 buffer32;
900 stream_->ReadVarint32(&buffer32);
901 result = SimpleItoa(bit_cast<int32>(buffer32));
902 break;
903 }
904 case google::protobuf::Field_Kind_TYPE_INT64: {
905 uint64 buffer64;
906 stream_->ReadVarint64(&buffer64);
907 result = SimpleItoa(bit_cast<int64>(buffer64));
908 break;
909 }
910 case google::protobuf::Field_Kind_TYPE_UINT32: {
911 uint32 buffer32;
912 stream_->ReadVarint32(&buffer32);
913 result = SimpleItoa(bit_cast<uint32>(buffer32));
914 break;
915 }
916 case google::protobuf::Field_Kind_TYPE_UINT64: {
917 uint64 buffer64;
918 stream_->ReadVarint64(&buffer64);
919 result = SimpleItoa(bit_cast<uint64>(buffer64));
920 break;
921 }
922 case google::protobuf::Field_Kind_TYPE_SINT32: {
923 uint32 buffer32;
924 stream_->ReadVarint32(&buffer32);
925 result = SimpleItoa(WireFormatLite::ZigZagDecode32(buffer32));
926 break;
927 }
928 case google::protobuf::Field_Kind_TYPE_SINT64: {
929 uint64 buffer64;
930 stream_->ReadVarint64(&buffer64);
931 result = SimpleItoa(WireFormatLite::ZigZagDecode64(buffer64));
932 break;
933 }
934 case google::protobuf::Field_Kind_TYPE_SFIXED32: {
935 uint32 buffer32;
936 stream_->ReadLittleEndian32(&buffer32);
937 result = SimpleItoa(bit_cast<int32>(buffer32));
938 break;
939 }
940 case google::protobuf::Field_Kind_TYPE_SFIXED64: {
941 uint64 buffer64;
942 stream_->ReadLittleEndian64(&buffer64);
943 result = SimpleItoa(bit_cast<int64>(buffer64));
944 break;
945 }
946 case google::protobuf::Field_Kind_TYPE_FIXED32: {
947 uint32 buffer32;
948 stream_->ReadLittleEndian32(&buffer32);
949 result = SimpleItoa(bit_cast<uint32>(buffer32));
950 break;
951 }
952 case google::protobuf::Field_Kind_TYPE_FIXED64: {
953 uint64 buffer64;
954 stream_->ReadLittleEndian64(&buffer64);
955 result = SimpleItoa(bit_cast<uint64>(buffer64));
956 break;
957 }
958 case google::protobuf::Field_Kind_TYPE_FLOAT: {
959 uint32 buffer32;
960 stream_->ReadLittleEndian32(&buffer32);
961 result = SimpleFtoa(bit_cast<float>(buffer32));
962 break;
963 }
964 case google::protobuf::Field_Kind_TYPE_DOUBLE: {
965 uint64 buffer64;
966 stream_->ReadLittleEndian64(&buffer64);
967 result = SimpleDtoa(bit_cast<double>(buffer64));
968 break;
969 }
970 case google::protobuf::Field_Kind_TYPE_ENUM: {
971 uint32 buffer32;
972 stream_->ReadVarint32(&buffer32);
973 // Get the nested enum type for this field.
974 // TODO(skarvaje): Avoid string manipulation. Find ways to speed this
975 // up.
976 const google::protobuf::Enum* en =
977 typeinfo_->GetEnumByTypeUrl(field.type_url());
978 // Lookup the name of the enum, and render that. Skips unknown enums.
979 if (en != NULL) {
980 const google::protobuf::EnumValue* enum_value =
981 FindEnumValueByNumber(*en, buffer32);
982 if (enum_value != NULL) {
983 result = enum_value->name();
984 }
985 }
986 break;
987 }
988 case google::protobuf::Field_Kind_TYPE_STRING: {
989 uint32 buffer32;
990 stream_->ReadVarint32(&buffer32); // string size.
991 stream_->ReadString(&result, buffer32);
992 break;
993 }
994 case google::protobuf::Field_Kind_TYPE_BYTES: {
995 uint32 buffer32;
996 stream_->ReadVarint32(&buffer32); // bytes size.
997 stream_->ReadString(&result, buffer32);
998 break;
999 }
1000 default:
1001 break;
1002 }
1003 return result;
1004 }
1005
1006 // Field is a map if it is a repeated message and it has an option "map_type".
1007 // TODO(skarvaje): Consider pre-computing the IsMap() into Field directly.
IsMap(const google::protobuf::Field & field) const1008 bool ProtoStreamObjectSource::IsMap(
1009 const google::protobuf::Field& field) const {
1010 const google::protobuf::Type* field_type =
1011 typeinfo_->GetTypeByTypeUrl(field.type_url());
1012
1013 // TODO(xiaofeng): Unify option names.
1014 return field.kind() == google::protobuf::Field_Kind_TYPE_MESSAGE &&
1015 (GetBoolOptionOrDefault(field_type->options(),
1016 "google.protobuf.MessageOptions.map_entry", false) ||
1017 GetBoolOptionOrDefault(field_type->options(), "map_entry", false));
1018 }
1019
ReadSecondsAndNanos(const google::protobuf::Type & type) const1020 std::pair<int64, int32> ProtoStreamObjectSource::ReadSecondsAndNanos(
1021 const google::protobuf::Type& type) const {
1022 uint64 seconds = 0;
1023 uint32 nanos = 0;
1024 uint32 tag = 0;
1025 int64 signed_seconds = 0;
1026 int32 signed_nanos = 0;
1027
1028 for (tag = stream_->ReadTag(); tag != 0; tag = stream_->ReadTag()) {
1029 const google::protobuf::Field* field = FindAndVerifyField(type, tag);
1030 if (field == NULL) {
1031 WireFormat::SkipField(stream_, tag, NULL);
1032 continue;
1033 }
1034 // 'seconds' has field number of 1 and 'nanos' has field number 2
1035 // //google/protobuf/timestamp.proto & duration.proto
1036 if (field->number() == 1) {
1037 // read seconds
1038 stream_->ReadVarint64(&seconds);
1039 signed_seconds = bit_cast<int64>(seconds);
1040 } else if (field->number() == 2) {
1041 // read nanos
1042 stream_->ReadVarint32(&nanos);
1043 signed_nanos = bit_cast<int32>(nanos);
1044 }
1045 }
1046 return std::pair<int64, int32>(signed_seconds, signed_nanos);
1047 }
1048
IncrementRecursionDepth(StringPiece type_name,StringPiece field_name) const1049 Status ProtoStreamObjectSource::IncrementRecursionDepth(
1050 StringPiece type_name, StringPiece field_name) const {
1051 if (++recursion_depth_ > max_recursion_depth_) {
1052 return Status(
1053 util::error::INVALID_ARGUMENT,
1054 StrCat("Message too deep. Max recursion depth reached for type '",
1055 type_name, "', field '", field_name, "'"));
1056 }
1057 return Status::OK;
1058 }
1059
1060 namespace {
1061 // TODO(skarvaje): Speed this up by not doing a linear scan.
FindFieldByNumber(const google::protobuf::Type & type,int number)1062 const google::protobuf::Field* FindFieldByNumber(
1063 const google::protobuf::Type& type, int number) {
1064 for (int i = 0; i < type.fields_size(); ++i) {
1065 if (type.fields(i).number() == number) {
1066 return &type.fields(i);
1067 }
1068 }
1069 return NULL;
1070 }
1071
1072 // TODO(skarvaje): Replace FieldDescriptor by implementing IsTypePackable()
1073 // using tech Field.
IsPackable(const google::protobuf::Field & field)1074 bool IsPackable(const google::protobuf::Field& field) {
1075 return field.cardinality() ==
1076 google::protobuf::Field_Cardinality_CARDINALITY_REPEATED &&
1077 google::protobuf::FieldDescriptor::IsTypePackable(
1078 static_cast<google::protobuf::FieldDescriptor::Type>(field.kind()));
1079 }
1080
1081 // TODO(skarvaje): Speed this up by not doing a linear scan.
FindEnumValueByNumber(const google::protobuf::Enum & tech_enum,int number)1082 const google::protobuf::EnumValue* FindEnumValueByNumber(
1083 const google::protobuf::Enum& tech_enum, int number) {
1084 for (int i = 0; i < tech_enum.enumvalue_size(); ++i) {
1085 const google::protobuf::EnumValue& ev = tech_enum.enumvalue(i);
1086 if (ev.number() == number) {
1087 return &ev;
1088 }
1089 }
1090 return NULL;
1091 }
1092
1093 // TODO(skarvaje): Look into optimizing this by not doing computation on
1094 // double.
FormatNanos(uint32 nanos)1095 const string FormatNanos(uint32 nanos) {
1096 const char* format =
1097 (nanos % 1000 != 0) ? "%.9f" : (nanos % 1000000 != 0) ? "%.6f" : "%.3f";
1098 string formatted =
1099 StringPrintf(format, static_cast<double>(nanos) / kNanosPerSecond);
1100 // remove the leading 0 before decimal.
1101 return formatted.substr(1);
1102 }
1103 } // namespace
1104
1105 } // namespace converter
1106 } // namespace util
1107 } // namespace protobuf
1108 } // namespace google
1109