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