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