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