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