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