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