• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 #include <google/protobuf/util/internal/protostream_objectsource.h>
32 
33 #include <cstdint>
34 #include <unordered_map>
35 #include <utility>
36 
37 #include <google/protobuf/stubs/logging.h>
38 #include <google/protobuf/stubs/common.h>
39 #include <google/protobuf/io/coded_stream.h>
40 #include <google/protobuf/io/zero_copy_stream_impl.h>
41 #include <google/protobuf/descriptor.h>
42 #include <google/protobuf/stubs/once.h>
43 #include <google/protobuf/unknown_field_set.h>
44 #include <google/protobuf/wire_format.h>
45 #include <google/protobuf/wire_format_lite.h>
46 #include <google/protobuf/stubs/strutil.h>
47 #include <google/protobuf/stubs/casts.h>
48 #include <google/protobuf/stubs/status.h>
49 #include <google/protobuf/stubs/stringprintf.h>
50 #include <google/protobuf/stubs/time.h>
51 #include <google/protobuf/util/internal/constants.h>
52 #include <google/protobuf/util/internal/field_mask_utility.h>
53 #include <google/protobuf/util/internal/utility.h>
54 #include <google/protobuf/stubs/map_util.h>
55 #include <google/protobuf/stubs/status_macros.h>
56 
57 
58 // Must be included last.
59 #include <google/protobuf/port_def.inc>
60 
61 
62 namespace google {
63 namespace protobuf {
64 namespace util {
65 namespace converter {
66 
67 using ::PROTOBUF_NAMESPACE_ID::internal::WireFormat;
68 using ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite;
69 
70 namespace {
71 
72 static int kDefaultMaxRecursionDepth = 64;
73 
74 // Finds a field with the given number. nullptr if none found.
75 const google::protobuf::Field* FindFieldByNumber(
76     const google::protobuf::Type& type, int number);
77 
78 // Returns true if the field is packable.
79 bool IsPackable(const google::protobuf::Field& field);
80 
81 // Finds an enum value with the given number. nullptr if none found.
82 const google::protobuf::EnumValue* FindEnumValueByNumber(
83     const google::protobuf::Enum& tech_enum, int number);
84 
85 // Utility function to format nanos.
86 const std::string FormatNanos(uint32_t nanos, bool with_trailing_zeros);
87 
MapKeyDefaultValueAsString(const google::protobuf::Field & field)88 util::StatusOr<std::string> MapKeyDefaultValueAsString(
89     const google::protobuf::Field& field) {
90   switch (field.kind()) {
91     case google::protobuf::Field::TYPE_BOOL:
92       return std::string("false");
93     case google::protobuf::Field::TYPE_INT32:
94     case google::protobuf::Field::TYPE_INT64:
95     case google::protobuf::Field::TYPE_UINT32:
96     case google::protobuf::Field::TYPE_UINT64:
97     case google::protobuf::Field::TYPE_SINT32:
98     case google::protobuf::Field::TYPE_SINT64:
99     case google::protobuf::Field::TYPE_SFIXED32:
100     case google::protobuf::Field::TYPE_SFIXED64:
101     case google::protobuf::Field::TYPE_FIXED32:
102     case google::protobuf::Field::TYPE_FIXED64:
103       return std::string("0");
104     case google::protobuf::Field::TYPE_STRING:
105       return std::string();
106     default:
107       return util::InternalError("Invalid map key type.");
108   }
109 }
110 }  // namespace
111 
112 
ProtoStreamObjectSource(io::CodedInputStream * stream,TypeResolver * type_resolver,const google::protobuf::Type & type,const RenderOptions & render_options)113 ProtoStreamObjectSource::ProtoStreamObjectSource(
114     io::CodedInputStream* stream, TypeResolver* type_resolver,
115     const google::protobuf::Type& type, const RenderOptions& render_options)
116     : stream_(stream),
117       typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
118       own_typeinfo_(true),
119       type_(type),
120       render_options_(render_options),
121       recursion_depth_(0),
122       max_recursion_depth_(kDefaultMaxRecursionDepth) {
123   GOOGLE_LOG_IF(DFATAL, stream == nullptr) << "Input stream is nullptr.";
124 }
125 
ProtoStreamObjectSource(io::CodedInputStream * stream,const TypeInfo * typeinfo,const google::protobuf::Type & type,const RenderOptions & render_options)126 ProtoStreamObjectSource::ProtoStreamObjectSource(
127     io::CodedInputStream* stream, const TypeInfo* typeinfo,
128     const google::protobuf::Type& type, const RenderOptions& render_options)
129     : stream_(stream),
130       typeinfo_(typeinfo),
131       own_typeinfo_(false),
132       type_(type),
133       render_options_(render_options),
134       recursion_depth_(0),
135       max_recursion_depth_(kDefaultMaxRecursionDepth) {
136   GOOGLE_LOG_IF(DFATAL, stream == nullptr) << "Input stream is nullptr.";
137 }
138 
~ProtoStreamObjectSource()139 ProtoStreamObjectSource::~ProtoStreamObjectSource() {
140   if (own_typeinfo_) {
141     delete typeinfo_;
142   }
143 }
144 
NamedWriteTo(StringPiece name,ObjectWriter * ow) const145 util::Status ProtoStreamObjectSource::NamedWriteTo(StringPiece name,
146                                                    ObjectWriter* ow) const {
147   return WriteMessage(type_, name, 0, true, ow);
148 }
149 
FindAndVerifyField(const google::protobuf::Type & type,uint32_t tag) const150 const google::protobuf::Field* ProtoStreamObjectSource::FindAndVerifyField(
151     const google::protobuf::Type& type, uint32_t tag) const {
152   // Lookup the new field in the type by tag number.
153   const google::protobuf::Field* field = FindFieldByNumber(type, tag >> 3);
154   // Verify if the field corresponds to the wire type in tag.
155   // If there is any discrepancy, mark the field as not found.
156   if (field != nullptr) {
157     WireFormatLite::WireType expected_type =
158         WireFormatLite::WireTypeForFieldType(
159             static_cast<WireFormatLite::FieldType>(field->kind()));
160     WireFormatLite::WireType actual_type = WireFormatLite::GetTagWireType(tag);
161     if (actual_type != expected_type &&
162         (!IsPackable(*field) ||
163          actual_type != WireFormatLite::WIRETYPE_LENGTH_DELIMITED)) {
164       field = nullptr;
165     }
166   }
167   return field;
168 }
169 
WriteMessage(const google::protobuf::Type & type,StringPiece name,const uint32_t end_tag,bool include_start_and_end,ObjectWriter * ow) const170 util::Status ProtoStreamObjectSource::WriteMessage(
171     const google::protobuf::Type& type, StringPiece name,
172     const uint32_t end_tag, bool include_start_and_end,
173     ObjectWriter* ow) const {
174 
175   const TypeRenderer* type_renderer = FindTypeRenderer(type.name());
176   if (type_renderer != nullptr) {
177     return (*type_renderer)(this, type, name, ow);
178   }
179 
180   const google::protobuf::Field* field = nullptr;
181   std::string field_name;
182   // last_tag set to dummy value that is different from tag.
183   uint32_t tag = stream_->ReadTag(), last_tag = tag + 1;
184   UnknownFieldSet unknown_fields;
185 
186 
187   if (include_start_and_end) {
188     ow->StartObject(name);
189   }
190   while (tag != end_tag && tag != 0) {
191     if (tag != last_tag) {  // Update field only if tag is changed.
192       last_tag = tag;
193       field = FindAndVerifyField(type, tag);
194       if (field != nullptr) {
195         if (render_options_.preserve_proto_field_names) {
196           field_name = field->name();
197         } else {
198           field_name = field->json_name();
199         }
200       }
201     }
202     if (field == nullptr) {
203       // If we didn't find a field, skip this unknown tag.
204       // TODO(wpoon): Check return boolean value.
205       WireFormat::SkipField(
206           stream_, tag,
207                                                 nullptr);
208       tag = stream_->ReadTag();
209       continue;
210     }
211 
212     if (field->cardinality() == google::protobuf::Field::CARDINALITY_REPEATED) {
213       if (IsMap(*field)) {
214         ow->StartObject(field_name);
215         ASSIGN_OR_RETURN(tag, RenderMap(field, field_name, tag, ow));
216         ow->EndObject();
217       } else {
218         ASSIGN_OR_RETURN(tag, RenderList(field, field_name, tag, ow));
219       }
220     } else {
221       // Render the field.
222       RETURN_IF_ERROR(RenderField(field, field_name, ow));
223       tag = stream_->ReadTag();
224     }
225   }
226 
227 
228   if (include_start_and_end) {
229     ow->EndObject();
230   }
231   return util::Status();
232 }
233 
RenderList(const google::protobuf::Field * field,StringPiece name,uint32_t list_tag,ObjectWriter * ow) const234 util::StatusOr<uint32_t> ProtoStreamObjectSource::RenderList(
235     const google::protobuf::Field* field, StringPiece name,
236     uint32_t list_tag, ObjectWriter* ow) const {
237   uint32_t tag_to_return = 0;
238   ow->StartList(name);
239   if (IsPackable(*field) &&
240       list_tag ==
241           WireFormatLite::MakeTag(field->number(),
242                                   WireFormatLite::WIRETYPE_LENGTH_DELIMITED)) {
243     RETURN_IF_ERROR(RenderPacked(field, ow));
244     // Since packed fields have a single tag, read another tag from stream to
245     // return.
246     tag_to_return = stream_->ReadTag();
247   } else {
248     do {
249       RETURN_IF_ERROR(RenderField(field, "", ow));
250     } while ((tag_to_return = stream_->ReadTag()) == list_tag);
251   }
252   ow->EndList();
253   return tag_to_return;
254 }
255 
RenderMap(const google::protobuf::Field * field,StringPiece,uint32_t list_tag,ObjectWriter * ow) const256 util::StatusOr<uint32_t> ProtoStreamObjectSource::RenderMap(
257     const google::protobuf::Field* field, StringPiece /* name */,
258     uint32_t list_tag, ObjectWriter* ow) const {
259   const google::protobuf::Type* field_type =
260       typeinfo_->GetTypeByTypeUrl(field->type_url());
261   uint32_t tag_to_return = 0;
262   do {
263     // Render map entry message type.
264     uint32_t buffer32;
265     stream_->ReadVarint32(&buffer32);  // message length
266     int old_limit = stream_->PushLimit(buffer32);
267     std::string map_key;
268     for (uint32_t tag = stream_->ReadTag(); tag != 0;
269          tag = stream_->ReadTag()) {
270       const google::protobuf::Field* map_entry_field =
271           FindAndVerifyField(*field_type, tag);
272       if (map_entry_field == nullptr) {
273         WireFormat::SkipField(stream_, tag, nullptr);
274         continue;
275       }
276       // Map field numbers are key = 1 and value = 2
277       if (map_entry_field->number() == 1) {
278         map_key = ReadFieldValueAsString(*map_entry_field);
279       } else if (map_entry_field->number() == 2) {
280         if (map_key.empty()) {
281           // An absent map key is treated as the default.
282           const google::protobuf::Field* key_field =
283               FindFieldByNumber(*field_type, 1);
284           if (key_field == nullptr) {
285             // The Type info for this map entry is incorrect. It should always
286             // have a field named "key" and with field number 1.
287             return util::InternalError("Invalid map entry.");
288           }
289           ASSIGN_OR_RETURN(map_key, MapKeyDefaultValueAsString(*key_field));
290         }
291         RETURN_IF_ERROR(RenderField(map_entry_field, map_key, ow));
292       } else {
293         // The Type info for this map entry is incorrect. It should contain
294         // exactly two fields with field number 1 and 2.
295         return util::InternalError("Invalid map entry.");
296       }
297     }
298     stream_->PopLimit(old_limit);
299   } while ((tag_to_return = stream_->ReadTag()) == list_tag);
300   return tag_to_return;
301 }
302 
RenderPacked(const google::protobuf::Field * field,ObjectWriter * ow) const303 util::Status ProtoStreamObjectSource::RenderPacked(
304     const google::protobuf::Field* field, ObjectWriter* ow) const {
305   uint32_t length;
306   stream_->ReadVarint32(&length);
307   int old_limit = stream_->PushLimit(length);
308   while (stream_->BytesUntilLimit() > 0) {
309     RETURN_IF_ERROR(RenderField(field, StringPiece(), ow));
310   }
311   stream_->PopLimit(old_limit);
312   return util::Status();
313 }
314 
RenderTimestamp(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)315 util::Status ProtoStreamObjectSource::RenderTimestamp(
316     const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
317     StringPiece field_name, ObjectWriter* ow) {
318   std::pair<int64_t, int32_t> p = os->ReadSecondsAndNanos(type);
319   int64_t seconds = p.first;
320   int32_t nanos = p.second;
321   if (seconds > kTimestampMaxSeconds || seconds < kTimestampMinSeconds) {
322     return util::InternalError(StrCat(
323         "Timestamp seconds exceeds limit for field: ", field_name));
324   }
325 
326   if (nanos < 0 || nanos >= kNanosPerSecond) {
327     return util::InternalError(
328         StrCat("Timestamp nanos exceeds limit for field: ", field_name));
329   }
330 
331   ow->RenderString(field_name,
332                    ::google::protobuf::internal::FormatTime(seconds, nanos));
333 
334   return util::Status();
335 }
336 
RenderDuration(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)337 util::Status ProtoStreamObjectSource::RenderDuration(
338     const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
339     StringPiece field_name, ObjectWriter* ow) {
340   std::pair<int64_t, int32_t> p = os->ReadSecondsAndNanos(type);
341   int64_t seconds = p.first;
342   int32_t nanos = p.second;
343   if (seconds > kDurationMaxSeconds || seconds < kDurationMinSeconds) {
344     return util::InternalError(
345         StrCat("Duration seconds exceeds limit for field: ", field_name));
346   }
347 
348   if (nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) {
349     return util::InternalError(
350         StrCat("Duration nanos exceeds limit for field: ", field_name));
351   }
352 
353   std::string sign = "";
354   if (seconds < 0) {
355     if (nanos > 0) {
356       return util::InternalError(
357           StrCat("Duration nanos is non-negative, but seconds is "
358                        "negative for field: ",
359                        field_name));
360     }
361     sign = "-";
362     seconds = -seconds;
363     nanos = -nanos;
364   } else if (seconds == 0 && nanos < 0) {
365     sign = "-";
366     nanos = -nanos;
367   }
368   std::string formatted_duration = StringPrintf(
369       "%s%lld%ss", sign.c_str(), static_cast<long long>(seconds),  // NOLINT
370       FormatNanos(
371           nanos,
372           false
373           )
374           .c_str());
375   ow->RenderString(field_name, formatted_duration);
376   return util::Status();
377 }
378 
RenderDouble(const ProtoStreamObjectSource * os,const google::protobuf::Type &,StringPiece field_name,ObjectWriter * ow)379 util::Status ProtoStreamObjectSource::RenderDouble(
380     const ProtoStreamObjectSource* os, const google::protobuf::Type& /*type*/,
381     StringPiece field_name, ObjectWriter* ow) {
382   uint32_t tag = os->stream_->ReadTag();
383   uint64_t buffer64 = 0;  // default value of Double wrapper value
384   if (tag != 0) {
385     os->stream_->ReadLittleEndian64(&buffer64);
386     os->stream_->ReadTag();
387   }
388   ow->RenderDouble(field_name, bit_cast<double>(buffer64));
389   return util::Status();
390 }
391 
RenderFloat(const ProtoStreamObjectSource * os,const google::protobuf::Type &,StringPiece field_name,ObjectWriter * ow)392 util::Status ProtoStreamObjectSource::RenderFloat(
393     const ProtoStreamObjectSource* os, const google::protobuf::Type& /*type*/,
394     StringPiece field_name, ObjectWriter* ow) {
395   uint32_t tag = os->stream_->ReadTag();
396   uint32_t buffer32 = 0;  // default value of Float wrapper value
397   if (tag != 0) {
398     os->stream_->ReadLittleEndian32(&buffer32);
399     os->stream_->ReadTag();
400   }
401   ow->RenderFloat(field_name, bit_cast<float>(buffer32));
402   return util::Status();
403 }
404 
RenderInt64(const ProtoStreamObjectSource * os,const google::protobuf::Type &,StringPiece field_name,ObjectWriter * ow)405 util::Status ProtoStreamObjectSource::RenderInt64(
406     const ProtoStreamObjectSource* os, const google::protobuf::Type& /*type*/,
407     StringPiece field_name, ObjectWriter* ow) {
408   uint32_t tag = os->stream_->ReadTag();
409   uint64_t buffer64 = 0;  // default value of Int64 wrapper value
410   if (tag != 0) {
411     os->stream_->ReadVarint64(&buffer64);
412     os->stream_->ReadTag();
413   }
414   ow->RenderInt64(field_name, bit_cast<int64_t>(buffer64));
415   return util::Status();
416 }
417 
RenderUInt64(const ProtoStreamObjectSource * os,const google::protobuf::Type &,StringPiece field_name,ObjectWriter * ow)418 util::Status ProtoStreamObjectSource::RenderUInt64(
419     const ProtoStreamObjectSource* os, const google::protobuf::Type& /*type*/,
420     StringPiece field_name, ObjectWriter* ow) {
421   uint32_t tag = os->stream_->ReadTag();
422   uint64_t buffer64 = 0;  // default value of UInt64 wrapper value
423   if (tag != 0) {
424     os->stream_->ReadVarint64(&buffer64);
425     os->stream_->ReadTag();
426   }
427   ow->RenderUint64(field_name, bit_cast<uint64_t>(buffer64));
428   return util::Status();
429 }
430 
RenderInt32(const ProtoStreamObjectSource * os,const google::protobuf::Type &,StringPiece field_name,ObjectWriter * ow)431 util::Status ProtoStreamObjectSource::RenderInt32(
432     const ProtoStreamObjectSource* os, const google::protobuf::Type& /*type*/,
433     StringPiece field_name, ObjectWriter* ow) {
434   uint32_t tag = os->stream_->ReadTag();
435   uint32_t buffer32 = 0;  // default value of Int32 wrapper value
436   if (tag != 0) {
437     os->stream_->ReadVarint32(&buffer32);
438     os->stream_->ReadTag();
439   }
440   ow->RenderInt32(field_name, bit_cast<int32_t>(buffer32));
441   return util::Status();
442 }
443 
RenderUInt32(const ProtoStreamObjectSource * os,const google::protobuf::Type &,StringPiece field_name,ObjectWriter * ow)444 util::Status ProtoStreamObjectSource::RenderUInt32(
445     const ProtoStreamObjectSource* os, const google::protobuf::Type& /*type*/,
446     StringPiece field_name, ObjectWriter* ow) {
447   uint32_t tag = os->stream_->ReadTag();
448   uint32_t 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_t>(buffer32));
454   return util::Status();
455 }
456 
RenderBool(const ProtoStreamObjectSource * os,const google::protobuf::Type &,StringPiece field_name,ObjectWriter * ow)457 util::Status ProtoStreamObjectSource::RenderBool(
458     const ProtoStreamObjectSource* os, const google::protobuf::Type& /*type*/,
459     StringPiece field_name, ObjectWriter* ow) {
460   uint32_t tag = os->stream_->ReadTag();
461   uint64_t buffer64 = 0;  // results in 'false' value as default, which is the
462                           // default value of Bool wrapper
463   if (tag != 0) {
464     os->stream_->ReadVarint64(&buffer64);
465     os->stream_->ReadTag();
466   }
467   ow->RenderBool(field_name, buffer64 != 0);
468   return util::Status();
469 }
470 
RenderString(const ProtoStreamObjectSource * os,const google::protobuf::Type &,StringPiece field_name,ObjectWriter * ow)471 util::Status ProtoStreamObjectSource::RenderString(
472     const ProtoStreamObjectSource* os, const google::protobuf::Type& /*type*/,
473     StringPiece field_name, ObjectWriter* ow) {
474   uint32_t tag = os->stream_->ReadTag();
475   uint32_t buffer32;
476   std::string str;  // default value of empty for String wrapper
477   if (tag != 0) {
478     os->stream_->ReadVarint32(&buffer32);  // string size.
479     os->stream_->ReadString(&str, buffer32);
480     os->stream_->ReadTag();
481   }
482   ow->RenderString(field_name, str);
483   return util::Status();
484 }
485 
RenderBytes(const ProtoStreamObjectSource * os,const google::protobuf::Type &,StringPiece field_name,ObjectWriter * ow)486 util::Status ProtoStreamObjectSource::RenderBytes(
487     const ProtoStreamObjectSource* os, const google::protobuf::Type& /*type*/,
488     StringPiece field_name, ObjectWriter* ow) {
489   uint32_t tag = os->stream_->ReadTag();
490   uint32_t buffer32;
491   std::string str;
492   if (tag != 0) {
493     os->stream_->ReadVarint32(&buffer32);
494     os->stream_->ReadString(&str, buffer32);
495     os->stream_->ReadTag();
496   }
497   ow->RenderBytes(field_name, str);
498   return util::Status();
499 }
500 
RenderStruct(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)501 util::Status ProtoStreamObjectSource::RenderStruct(
502     const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
503     StringPiece field_name, ObjectWriter* ow) {
504   const google::protobuf::Field* field = nullptr;
505   uint32_t tag = os->stream_->ReadTag();
506   ow->StartObject(field_name);
507   while (tag != 0) {
508     field = os->FindAndVerifyField(type, tag);
509     if (field == nullptr) {
510       WireFormat::SkipField(os->stream_, tag, nullptr);
511       tag = os->stream_->ReadTag();
512       continue;
513     }
514     // google.protobuf.Struct has only one field that is a map. Hence we use
515     // RenderMap to render that field.
516     if (os->IsMap(*field)) {
517       ASSIGN_OR_RETURN(tag, os->RenderMap(field, field_name, tag, ow));
518     }
519   }
520   ow->EndObject();
521   return util::Status();
522 }
523 
RenderStructValue(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)524 util::Status ProtoStreamObjectSource::RenderStructValue(
525     const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
526     StringPiece field_name, ObjectWriter* ow) {
527   const google::protobuf::Field* field = nullptr;
528   for (uint32_t tag = os->stream_->ReadTag(); tag != 0;
529        tag = os->stream_->ReadTag()) {
530     field = os->FindAndVerifyField(type, tag);
531     if (field == nullptr) {
532       WireFormat::SkipField(os->stream_, tag, nullptr);
533       continue;
534     }
535     RETURN_IF_ERROR(os->RenderField(field, field_name, ow));
536   }
537   return util::Status();
538 }
539 
540 // 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)541 util::Status ProtoStreamObjectSource::RenderStructListValue(
542     const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
543     StringPiece field_name, ObjectWriter* ow) {
544   uint32_t tag = os->stream_->ReadTag();
545 
546   // Render empty list when we find empty ListValue message.
547   if (tag == 0) {
548     ow->StartList(field_name);
549     ow->EndList();
550     return util::Status();
551   }
552 
553   while (tag != 0) {
554     const google::protobuf::Field* field = os->FindAndVerifyField(type, tag);
555     if (field == nullptr) {
556       WireFormat::SkipField(os->stream_, tag, nullptr);
557       tag = os->stream_->ReadTag();
558       continue;
559     }
560     ASSIGN_OR_RETURN(tag, os->RenderList(field, field_name, tag, ow));
561   }
562   return util::Status();
563 }
564 
RenderAny(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)565 util::Status ProtoStreamObjectSource::RenderAny(
566     const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
567     StringPiece field_name, ObjectWriter* ow) {
568   // An Any is of the form { string type_url = 1; bytes value = 2; }
569   uint32_t tag;
570   std::string type_url;
571   std::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 == nullptr) {
577       WireFormat::SkipField(os->stream_, tag, nullptr);
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_t 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_t 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();
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::InternalError("Invalid Any, the type_url is missing.");
610   }
611 
612   util::StatusOr<const google::protobuf::Type*> resolved_type =
613       os->typeinfo_->ResolveTypeUrl(type_url);
614 
615   if (!resolved_type.ok()) {
616     // Convert into an internal error, since this means the backend gave us
617     // an invalid response (missing or invalid type information).
618     return util::InternalError(resolved_type.status().message());
619   }
620   // nested_type cannot be null at this time.
621   const google::protobuf::Type* nested_type = resolved_type.value();
622 
623   io::ArrayInputStream zero_copy_stream(value.data(), value.size());
624   io::CodedInputStream in_stream(&zero_copy_stream);
625   // We know the type so we can render it. Recursively parse the nested stream
626   // using a nested ProtoStreamObjectSource using our nested type information.
627   ProtoStreamObjectSource nested_os(&in_stream, os->typeinfo_, *nested_type,
628                                     os->render_options_);
629 
630   // We manually call start and end object here so we can inject the @type.
631   ow->StartObject(field_name);
632   ow->RenderString("@type", type_url);
633   util::Status result =
634       nested_os.WriteMessage(nested_os.type_, "value", 0, false, ow);
635   ow->EndObject();
636   return result;
637 }
638 
RenderFieldMask(const ProtoStreamObjectSource * os,const google::protobuf::Type & type,StringPiece field_name,ObjectWriter * ow)639 util::Status ProtoStreamObjectSource::RenderFieldMask(
640     const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
641     StringPiece field_name, ObjectWriter* ow) {
642   std::string combined;
643   uint32_t buffer32;
644   uint32_t paths_field_tag = 0;
645   for (uint32_t tag = os->stream_->ReadTag(); tag != 0;
646        tag = os->stream_->ReadTag()) {
647     if (paths_field_tag == 0) {
648       const google::protobuf::Field* field = os->FindAndVerifyField(type, tag);
649       if (field != nullptr && field->number() == 1 &&
650           field->name() == "paths") {
651         paths_field_tag = tag;
652       }
653     }
654     if (paths_field_tag != tag) {
655       return util::InternalError("Invalid FieldMask, unexpected field.");
656     }
657     std::string str;
658     os->stream_->ReadVarint32(&buffer32);  // string size.
659     os->stream_->ReadString(&str, buffer32);
660     if (!combined.empty()) {
661       combined.append(",");
662     }
663     combined.append(ConvertFieldMaskPath(str, &ToCamelCase));
664   }
665   ow->RenderString(field_name, combined);
666   return util::Status();
667 }
668 
669 
670 std::unordered_map<std::string, ProtoStreamObjectSource::TypeRenderer>*
671     ProtoStreamObjectSource::renderers_ = nullptr;
672 PROTOBUF_NAMESPACE_ID::internal::once_flag source_renderers_init_;
673 
674 
InitRendererMap()675 void ProtoStreamObjectSource::InitRendererMap() {
676   renderers_ = new std::unordered_map<std::string,
677                                       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_ = nullptr;
715 }
716 
717 // static
718 ProtoStreamObjectSource::TypeRenderer*
FindTypeRenderer(const std::string & type_url)719 ProtoStreamObjectSource::FindTypeRenderer(const std::string& type_url) {
720   PROTOBUF_NAMESPACE_ID::internal::call_once(source_renderers_init_,
721                                              InitRendererMap);
722   return FindOrNull(*renderers_, type_url);
723 }
724 
RenderField(const google::protobuf::Field * field,StringPiece field_name,ObjectWriter * ow) const725 util::Status ProtoStreamObjectSource::RenderField(
726     const google::protobuf::Field* field, StringPiece field_name,
727     ObjectWriter* ow) const {
728   // Short-circuit message types as it tends to call WriteMessage recursively
729   // and ends up using a lot of stack space. Keep the stack usage of this
730   // message small in order to preserve stack space and not crash.
731   if (field->kind() == google::protobuf::Field::TYPE_MESSAGE) {
732     uint32_t buffer32;
733     stream_->ReadVarint32(&buffer32);  // message length
734     int old_limit = stream_->PushLimit(buffer32);
735     // Get the nested message type for this field.
736     const google::protobuf::Type* type =
737         typeinfo_->GetTypeByTypeUrl(field->type_url());
738     if (type == nullptr) {
739       return util::InternalError(
740           StrCat("Invalid configuration. Could not find the type: ",
741                        field->type_url()));
742     }
743 
744     // Short-circuit any special type rendering to save call-stack space.
745     const TypeRenderer* type_renderer = FindTypeRenderer(type->name());
746 
747     RETURN_IF_ERROR(IncrementRecursionDepth(type->name(), field_name));
748     if (type_renderer != nullptr) {
749       RETURN_IF_ERROR((*type_renderer)(this, *type, field_name, ow));
750     } else {
751       RETURN_IF_ERROR(WriteMessage(*type, field_name, 0, true, ow));
752     }
753     --recursion_depth_;
754 
755     if (!stream_->ConsumedEntireMessage()) {
756       return util::InvalidArgumentError(
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 util::Status();
765 }
766 
RenderNonMessageField(const google::protobuf::Field * field,StringPiece field_name,ObjectWriter * ow) const767 util::Status ProtoStreamObjectSource::RenderNonMessageField(
768     const google::protobuf::Field* field, StringPiece field_name,
769     ObjectWriter* ow) const {
770   // Temporary buffers of different types.
771   uint32_t buffer32 = 0;
772   uint64_t buffer64 = 0;
773   std::string strbuffer;
774   switch (field->kind()) {
775     case google::protobuf::Field::TYPE_BOOL: {
776       stream_->ReadVarint64(&buffer64);
777       ow->RenderBool(field_name, buffer64 != 0);
778       break;
779     }
780     case google::protobuf::Field::TYPE_INT32: {
781       stream_->ReadVarint32(&buffer32);
782       ow->RenderInt32(field_name, bit_cast<int32_t>(buffer32));
783       break;
784     }
785     case google::protobuf::Field::TYPE_INT64: {
786       stream_->ReadVarint64(&buffer64);
787       ow->RenderInt64(field_name, bit_cast<int64_t>(buffer64));
788       break;
789     }
790     case google::protobuf::Field::TYPE_UINT32: {
791       stream_->ReadVarint32(&buffer32);
792       ow->RenderUint32(field_name, bit_cast<uint32_t>(buffer32));
793       break;
794     }
795     case google::protobuf::Field::TYPE_UINT64: {
796       stream_->ReadVarint64(&buffer64);
797       ow->RenderUint64(field_name, bit_cast<uint64_t>(buffer64));
798       break;
799     }
800     case google::protobuf::Field::TYPE_SINT32: {
801       stream_->ReadVarint32(&buffer32);
802       ow->RenderInt32(field_name, WireFormatLite::ZigZagDecode32(buffer32));
803       break;
804     }
805     case google::protobuf::Field::TYPE_SINT64: {
806       stream_->ReadVarint64(&buffer64);
807       ow->RenderInt64(field_name, WireFormatLite::ZigZagDecode64(buffer64));
808       break;
809     }
810     case google::protobuf::Field::TYPE_SFIXED32: {
811       stream_->ReadLittleEndian32(&buffer32);
812       ow->RenderInt32(field_name, bit_cast<int32_t>(buffer32));
813       break;
814     }
815     case google::protobuf::Field::TYPE_SFIXED64: {
816       stream_->ReadLittleEndian64(&buffer64);
817       ow->RenderInt64(field_name, bit_cast<int64_t>(buffer64));
818       break;
819     }
820     case google::protobuf::Field::TYPE_FIXED32: {
821       stream_->ReadLittleEndian32(&buffer32);
822       ow->RenderUint32(field_name, bit_cast<uint32_t>(buffer32));
823       break;
824     }
825     case google::protobuf::Field::TYPE_FIXED64: {
826       stream_->ReadLittleEndian64(&buffer64);
827       ow->RenderUint64(field_name, bit_cast<uint64_t>(buffer64));
828       break;
829     }
830     case google::protobuf::Field::TYPE_FLOAT: {
831       stream_->ReadLittleEndian32(&buffer32);
832       ow->RenderFloat(field_name, bit_cast<float>(buffer32));
833       break;
834     }
835     case google::protobuf::Field::TYPE_DOUBLE: {
836       stream_->ReadLittleEndian64(&buffer64);
837       ow->RenderDouble(field_name, bit_cast<double>(buffer64));
838       break;
839     }
840     case google::protobuf::Field::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. Unknown enum values
855       // are printed as integers.
856       if (en != nullptr) {
857         const google::protobuf::EnumValue* enum_value =
858             FindEnumValueByNumber(*en, buffer32);
859         if (enum_value != nullptr) {
860           if (render_options_.use_ints_for_enums) {
861             ow->RenderInt32(field_name, buffer32);
862           } else if (render_options_.use_lower_camel_for_enums) {
863             ow->RenderString(field_name,
864                              EnumValueNameToLowerCamelCase(enum_value->name()));
865           } else {
866             ow->RenderString(field_name, enum_value->name());
867           }
868         } else {
869             ow->RenderInt32(field_name, buffer32);
870         }
871       } else {
872           ow->RenderInt32(field_name, buffer32);
873       }
874       break;
875     }
876     case google::protobuf::Field::TYPE_STRING: {
877       stream_->ReadVarint32(&buffer32);  // string size.
878       stream_->ReadString(&strbuffer, buffer32);
879       ow->RenderString(field_name, strbuffer);
880       break;
881     }
882     case google::protobuf::Field::TYPE_BYTES: {
883       stream_->ReadVarint32(&buffer32);  // bytes size.
884       stream_->ReadString(&strbuffer, buffer32);
885       ow->RenderBytes(field_name, strbuffer);
886       break;
887     }
888     default:
889       break;
890   }
891   return util::Status();
892 }
893 
894 // TODO(skarvaje): Fix this to avoid code duplication.
ReadFieldValueAsString(const google::protobuf::Field & field) const895 const std::string ProtoStreamObjectSource::ReadFieldValueAsString(
896     const google::protobuf::Field& field) const {
897   std::string result;
898   switch (field.kind()) {
899     case google::protobuf::Field::TYPE_BOOL: {
900       uint64_t buffer64;
901       stream_->ReadVarint64(&buffer64);
902       result = buffer64 != 0 ? "true" : "false";
903       break;
904     }
905     case google::protobuf::Field::TYPE_INT32: {
906       uint32_t buffer32;
907       stream_->ReadVarint32(&buffer32);
908       result = StrCat(bit_cast<int32_t>(buffer32));
909       break;
910     }
911     case google::protobuf::Field::TYPE_INT64: {
912       uint64_t buffer64;
913       stream_->ReadVarint64(&buffer64);
914       result = StrCat(bit_cast<int64_t>(buffer64));
915       break;
916     }
917     case google::protobuf::Field::TYPE_UINT32: {
918       uint32_t buffer32;
919       stream_->ReadVarint32(&buffer32);
920       result = StrCat(bit_cast<uint32_t>(buffer32));
921       break;
922     }
923     case google::protobuf::Field::TYPE_UINT64: {
924       uint64_t buffer64;
925       stream_->ReadVarint64(&buffer64);
926       result = StrCat(bit_cast<uint64_t>(buffer64));
927       break;
928     }
929     case google::protobuf::Field::TYPE_SINT32: {
930       uint32_t buffer32;
931       stream_->ReadVarint32(&buffer32);
932       result = StrCat(WireFormatLite::ZigZagDecode32(buffer32));
933       break;
934     }
935     case google::protobuf::Field::TYPE_SINT64: {
936       uint64_t buffer64;
937       stream_->ReadVarint64(&buffer64);
938       result = StrCat(WireFormatLite::ZigZagDecode64(buffer64));
939       break;
940     }
941     case google::protobuf::Field::TYPE_SFIXED32: {
942       uint32_t buffer32;
943       stream_->ReadLittleEndian32(&buffer32);
944       result = StrCat(bit_cast<int32_t>(buffer32));
945       break;
946     }
947     case google::protobuf::Field::TYPE_SFIXED64: {
948       uint64_t buffer64;
949       stream_->ReadLittleEndian64(&buffer64);
950       result = StrCat(bit_cast<int64_t>(buffer64));
951       break;
952     }
953     case google::protobuf::Field::TYPE_FIXED32: {
954       uint32_t buffer32;
955       stream_->ReadLittleEndian32(&buffer32);
956       result = StrCat(bit_cast<uint32_t>(buffer32));
957       break;
958     }
959     case google::protobuf::Field::TYPE_FIXED64: {
960       uint64_t buffer64;
961       stream_->ReadLittleEndian64(&buffer64);
962       result = StrCat(bit_cast<uint64_t>(buffer64));
963       break;
964     }
965     case google::protobuf::Field::TYPE_FLOAT: {
966       uint32_t buffer32;
967       stream_->ReadLittleEndian32(&buffer32);
968       result = SimpleFtoa(bit_cast<float>(buffer32));
969       break;
970     }
971     case google::protobuf::Field::TYPE_DOUBLE: {
972       uint64_t buffer64;
973       stream_->ReadLittleEndian64(&buffer64);
974       result = SimpleDtoa(bit_cast<double>(buffer64));
975       break;
976     }
977     case google::protobuf::Field::TYPE_ENUM: {
978       uint32_t buffer32;
979       stream_->ReadVarint32(&buffer32);
980       // Get the nested enum type for this field.
981       // TODO(skarvaje): Avoid string manipulation. Find ways to speed this
982       // up.
983       const google::protobuf::Enum* en =
984           typeinfo_->GetEnumByTypeUrl(field.type_url());
985       // Lookup the name of the enum, and render that. Skips unknown enums.
986       if (en != nullptr) {
987         const google::protobuf::EnumValue* enum_value =
988             FindEnumValueByNumber(*en, buffer32);
989         if (enum_value != nullptr) {
990           result = enum_value->name();
991         }
992       }
993       break;
994     }
995     case google::protobuf::Field::TYPE_STRING: {
996       uint32_t buffer32;
997       stream_->ReadVarint32(&buffer32);  // string size.
998       stream_->ReadString(&result, buffer32);
999       break;
1000     }
1001     case google::protobuf::Field::TYPE_BYTES: {
1002       uint32_t buffer32;
1003       stream_->ReadVarint32(&buffer32);  // bytes size.
1004       stream_->ReadString(&result, buffer32);
1005       break;
1006     }
1007     default:
1008       break;
1009   }
1010   return result;
1011 }
1012 
1013 // Field is a map if it is a repeated message and it has an option "map_type".
1014 // TODO(skarvaje): Consider pre-computing the IsMap() into Field directly.
IsMap(const google::protobuf::Field & field) const1015 bool ProtoStreamObjectSource::IsMap(
1016     const google::protobuf::Field& field) const {
1017   const google::protobuf::Type* field_type =
1018       typeinfo_->GetTypeByTypeUrl(field.type_url());
1019   return field.kind() == google::protobuf::Field::TYPE_MESSAGE &&
1020          util::converter::IsMap(field, *field_type);
1021 }
1022 
ReadSecondsAndNanos(const google::protobuf::Type & type) const1023 std::pair<int64_t, int32_t> ProtoStreamObjectSource::ReadSecondsAndNanos(
1024     const google::protobuf::Type& type) const {
1025   uint64_t seconds = 0;
1026   uint32_t nanos = 0;
1027   uint32_t tag = 0;
1028   int64_t signed_seconds = 0;
1029   int32_t signed_nanos = 0;
1030 
1031   for (tag = stream_->ReadTag(); tag != 0; tag = stream_->ReadTag()) {
1032     const google::protobuf::Field* field = FindAndVerifyField(type, tag);
1033     if (field == nullptr) {
1034       WireFormat::SkipField(stream_, tag, nullptr);
1035       continue;
1036     }
1037     // 'seconds' has field number of 1 and 'nanos' has field number 2
1038     // //google/protobuf/timestamp.proto & duration.proto
1039     if (field->number() == 1) {
1040       // read seconds
1041       stream_->ReadVarint64(&seconds);
1042       signed_seconds = bit_cast<int64_t>(seconds);
1043     } else if (field->number() == 2) {
1044       // read nanos
1045       stream_->ReadVarint32(&nanos);
1046       signed_nanos = bit_cast<int32_t>(nanos);
1047     }
1048   }
1049   return std::pair<int64_t, int32_t>(signed_seconds, signed_nanos);
1050 }
1051 
IncrementRecursionDepth(StringPiece type_name,StringPiece field_name) const1052 util::Status ProtoStreamObjectSource::IncrementRecursionDepth(
1053     StringPiece type_name, StringPiece field_name) const {
1054   if (++recursion_depth_ > max_recursion_depth_) {
1055     return util::InvalidArgumentError(
1056         StrCat("Message too deep. Max recursion depth reached for type '",
1057                      type_name, "', field '", field_name, "'"));
1058   }
1059   return util::Status();
1060 }
1061 
1062 namespace {
1063 // TODO(skarvaje): Speed this up by not doing a linear scan.
FindFieldByNumber(const google::protobuf::Type & type,int number)1064 const google::protobuf::Field* FindFieldByNumber(
1065     const google::protobuf::Type& type, int number) {
1066   for (int i = 0; i < type.fields_size(); ++i) {
1067     if (type.fields(i).number() == number) {
1068       return &type.fields(i);
1069     }
1070   }
1071   return nullptr;
1072 }
1073 
1074 // TODO(skarvaje): Replace FieldDescriptor by implementing IsTypePackable()
1075 // using tech Field.
IsPackable(const google::protobuf::Field & field)1076 bool IsPackable(const google::protobuf::Field& field) {
1077   return field.cardinality() == google::protobuf::Field::CARDINALITY_REPEATED &&
1078          FieldDescriptor::IsTypePackable(
1079              static_cast<FieldDescriptor::Type>(field.kind()));
1080 }
1081 
1082 // TODO(skarvaje): Speed this up by not doing a linear scan.
FindEnumValueByNumber(const google::protobuf::Enum & tech_enum,int number)1083 const google::protobuf::EnumValue* FindEnumValueByNumber(
1084     const google::protobuf::Enum& tech_enum, int number) {
1085   for (int i = 0; i < tech_enum.enumvalue_size(); ++i) {
1086     const google::protobuf::EnumValue& ev = tech_enum.enumvalue(i);
1087     if (ev.number() == number) {
1088       return &ev;
1089     }
1090   }
1091   return nullptr;
1092 }
1093 
1094 // TODO(skarvaje): Look into optimizing this by not doing computation on
1095 // double.
FormatNanos(uint32_t nanos,bool with_trailing_zeros)1096 const std::string FormatNanos(uint32_t nanos, bool with_trailing_zeros) {
1097   if (nanos == 0) {
1098     return with_trailing_zeros ? ".000" : "";
1099   }
1100 
1101   const int precision = (nanos % 1000 != 0)      ? 9
1102                         : (nanos % 1000000 != 0) ? 6
1103                                                  : 3;
1104   std::string formatted = StringPrintf(
1105       "%.*f", precision, 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