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