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