• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 #include <google/protobuf/util/internal/protostream_objectwriter.h>
32 
33 #include <cstdint>
34 #include <functional>
35 #include <stack>
36 #include <unordered_map>
37 #include <unordered_set>
38 
39 #include <google/protobuf/stubs/once.h>
40 #include <google/protobuf/wire_format_lite.h>
41 #include <google/protobuf/stubs/strutil.h>
42 #include <google/protobuf/stubs/status.h>
43 #include <google/protobuf/stubs/statusor.h>
44 #include <google/protobuf/stubs/time.h>
45 #include <google/protobuf/util/internal/constants.h>
46 #include <google/protobuf/util/internal/field_mask_utility.h>
47 #include <google/protobuf/util/internal/object_location_tracker.h>
48 #include <google/protobuf/util/internal/utility.h>
49 #include <google/protobuf/stubs/map_util.h>
50 
51 
52 // Must be included last.
53 #include <google/protobuf/port_def.inc>
54 
55 namespace google {
56 namespace protobuf {
57 namespace util {
58 namespace converter {
59 
60 using util::Status;
61 using ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite;
62 using std::placeholders::_1;
63 
64 
ProtoStreamObjectWriter(TypeResolver * type_resolver,const google::protobuf::Type & type,strings::ByteSink * output,ErrorListener * listener,const ProtoStreamObjectWriter::Options & options)65 ProtoStreamObjectWriter::ProtoStreamObjectWriter(
66     TypeResolver* type_resolver, const google::protobuf::Type& type,
67     strings::ByteSink* output, ErrorListener* listener,
68     const ProtoStreamObjectWriter::Options& options)
69     : ProtoWriter(type_resolver, type, output, listener),
70       master_type_(type),
71       current_(nullptr),
72       options_(options) {
73   set_ignore_unknown_fields(options_.ignore_unknown_fields);
74   set_ignore_unknown_enum_values(options_.ignore_unknown_enum_values);
75   set_use_lower_camel_for_enums(options_.use_lower_camel_for_enums);
76   set_case_insensitive_enum_parsing(options_.case_insensitive_enum_parsing);
77   set_use_json_name_in_missing_fields(options.use_json_name_in_missing_fields);
78 }
79 
ProtoStreamObjectWriter(const TypeInfo * typeinfo,const google::protobuf::Type & type,strings::ByteSink * output,ErrorListener * listener,const ProtoStreamObjectWriter::Options & options)80 ProtoStreamObjectWriter::ProtoStreamObjectWriter(
81     const TypeInfo* typeinfo, const google::protobuf::Type& type,
82     strings::ByteSink* output, ErrorListener* listener,
83     const ProtoStreamObjectWriter::Options& options)
84     : ProtoWriter(typeinfo, type, output, listener),
85       master_type_(type),
86       current_(nullptr),
87       options_(options) {
88   set_ignore_unknown_fields(options_.ignore_unknown_fields);
89   set_use_lower_camel_for_enums(options.use_lower_camel_for_enums);
90   set_case_insensitive_enum_parsing(options_.case_insensitive_enum_parsing);
91   set_use_json_name_in_missing_fields(options.use_json_name_in_missing_fields);
92 }
93 
ProtoStreamObjectWriter(const TypeInfo * typeinfo,const google::protobuf::Type & type,strings::ByteSink * output,ErrorListener * listener)94 ProtoStreamObjectWriter::ProtoStreamObjectWriter(
95     const TypeInfo* typeinfo, const google::protobuf::Type& type,
96     strings::ByteSink* output, ErrorListener* listener)
97     : ProtoWriter(typeinfo, type, output, listener),
98       master_type_(type),
99       current_(nullptr),
100       options_(ProtoStreamObjectWriter::Options::Defaults()) {}
101 
~ProtoStreamObjectWriter()102 ProtoStreamObjectWriter::~ProtoStreamObjectWriter() {
103   if (current_ == nullptr) return;
104   // Cleanup explicitly in order to avoid destructor stack overflow when input
105   // is deeply nested.
106   // Cast to BaseElement to avoid doing additional checks (like missing fields)
107   // during pop().
108   std::unique_ptr<BaseElement> element(
109       static_cast<BaseElement*>(current_.get())->pop<BaseElement>());
110   while (element != nullptr) {
111     element.reset(element->pop<BaseElement>());
112   }
113 }
114 
115 namespace {
116 // Utility method to split a string representation of Timestamp or Duration and
117 // return the parts.
SplitSecondsAndNanos(StringPiece input,StringPiece * seconds,StringPiece * nanos)118 void SplitSecondsAndNanos(StringPiece input, StringPiece* seconds,
119                           StringPiece* nanos) {
120   size_t idx = input.rfind('.');
121   if (idx != std::string::npos) {
122     *seconds = input.substr(0, idx);
123     *nanos = input.substr(idx + 1);
124   } else {
125     *seconds = input;
126     *nanos = StringPiece();
127   }
128 }
129 
GetNanosFromStringPiece(StringPiece s_nanos,const char * parse_failure_message,const char * exceeded_limit_message,int32_t * nanos)130 Status GetNanosFromStringPiece(StringPiece s_nanos,
131                                const char* parse_failure_message,
132                                const char* exceeded_limit_message,
133                                int32_t* nanos) {
134   *nanos = 0;
135 
136   // Count the number of leading 0s and consume them.
137   int num_leading_zeros = 0;
138   while (s_nanos.Consume("0")) {
139     num_leading_zeros++;
140   }
141   int32_t i_nanos = 0;
142   // 's_nanos' contains fractional seconds -- i.e. 'nanos' is equal to
143   // "0." + s_nanos.ToString() seconds. An int32_t is used for the
144   // conversion to 'nanos', rather than a double, so that there is no
145   // loss of precision.
146   if (!s_nanos.empty() && !safe_strto32(s_nanos, &i_nanos)) {
147     return util::InvalidArgumentError(parse_failure_message);
148   }
149   if (i_nanos > kNanosPerSecond || i_nanos < 0) {
150     return util::InvalidArgumentError(exceeded_limit_message);
151   }
152   // s_nanos should only have digits. No whitespace.
153   if (s_nanos.find_first_not_of("0123456789") != StringPiece::npos) {
154     return util::InvalidArgumentError(parse_failure_message);
155   }
156 
157   if (i_nanos > 0) {
158     // 'scale' is the number of digits to the right of the decimal
159     // point in "0." + s_nanos.ToString()
160     int32_t scale = num_leading_zeros + s_nanos.size();
161     // 'conversion' converts i_nanos into nanoseconds.
162     // conversion = kNanosPerSecond / static_cast<int32_t>(std::pow(10, scale))
163     // For efficiency, we precompute the conversion factor.
164     int32_t conversion = 0;
165     switch (scale) {
166       case 1:
167         conversion = 100000000;
168         break;
169       case 2:
170         conversion = 10000000;
171         break;
172       case 3:
173         conversion = 1000000;
174         break;
175       case 4:
176         conversion = 100000;
177         break;
178       case 5:
179         conversion = 10000;
180         break;
181       case 6:
182         conversion = 1000;
183         break;
184       case 7:
185         conversion = 100;
186         break;
187       case 8:
188         conversion = 10;
189         break;
190       case 9:
191         conversion = 1;
192         break;
193       default:
194         return util::InvalidArgumentError(exceeded_limit_message);
195     }
196     *nanos = i_nanos * conversion;
197   }
198 
199   return Status();
200 }
201 
202 }  // namespace
203 
AnyWriter(ProtoStreamObjectWriter * parent)204 ProtoStreamObjectWriter::AnyWriter::AnyWriter(ProtoStreamObjectWriter* parent)
205     : parent_(parent),
206       ow_(),
207       invalid_(false),
208       data_(),
209       output_(&data_),
210       depth_(0),
211       is_well_known_type_(false),
212       well_known_type_render_(nullptr) {}
213 
~AnyWriter()214 ProtoStreamObjectWriter::AnyWriter::~AnyWriter() {}
215 
StartObject(StringPiece name)216 void ProtoStreamObjectWriter::AnyWriter::StartObject(StringPiece name) {
217   ++depth_;
218   // If an object writer is absent, that means we have not called StartAny()
219   // before reaching here, which happens when we have data before the "@type"
220   // field.
221   if (ow_ == nullptr) {
222     // Save data before the "@type" field for later replay.
223     uninterpreted_events_.push_back(Event(Event::START_OBJECT, name));
224   } else if (is_well_known_type_ && depth_ == 1) {
225     // For well-known types, the only other field besides "@type" should be a
226     // "value" field.
227     if (name != "value" && !invalid_) {
228       parent_->InvalidValue("Any",
229                             "Expect a \"value\" field for well-known types.");
230       invalid_ = true;
231     }
232     ow_->StartObject("");
233   } else {
234     // Forward the call to the child writer if:
235     //   1. the type is not a well-known type.
236     //   2. or, we are in a nested Any, Struct, or Value object.
237     ow_->StartObject(name);
238   }
239 }
240 
EndObject()241 bool ProtoStreamObjectWriter::AnyWriter::EndObject() {
242   --depth_;
243   if (ow_ == nullptr) {
244     if (depth_ >= 0) {
245       // Save data before the "@type" field for later replay.
246       uninterpreted_events_.push_back(Event(Event::END_OBJECT));
247     }
248   } else if (depth_ >= 0 || !is_well_known_type_) {
249     // As long as depth_ >= 0, we know we haven't reached the end of Any.
250     // Propagate these EndObject() calls to the contained ow_. For regular
251     // message types, we propagate the end of Any as well.
252     ow_->EndObject();
253   }
254   // A negative depth_ implies that we have reached the end of Any
255   // object. Now we write out its contents.
256   if (depth_ < 0) {
257     WriteAny();
258     return false;
259   }
260   return true;
261 }
262 
StartList(StringPiece name)263 void ProtoStreamObjectWriter::AnyWriter::StartList(StringPiece name) {
264   ++depth_;
265   if (ow_ == nullptr) {
266     // Save data before the "@type" field for later replay.
267     uninterpreted_events_.push_back(Event(Event::START_LIST, name));
268   } else if (is_well_known_type_ && depth_ == 1) {
269     if (name != "value" && !invalid_) {
270       parent_->InvalidValue("Any",
271                             "Expect a \"value\" field for well-known types.");
272       invalid_ = true;
273     }
274     ow_->StartList("");
275   } else {
276     ow_->StartList(name);
277   }
278 }
279 
EndList()280 void ProtoStreamObjectWriter::AnyWriter::EndList() {
281   --depth_;
282   if (depth_ < 0) {
283     GOOGLE_LOG(DFATAL) << "Mismatched EndList found, should not be possible";
284     depth_ = 0;
285   }
286   if (ow_ == nullptr) {
287     // Save data before the "@type" field for later replay.
288     uninterpreted_events_.push_back(Event(Event::END_LIST));
289   } else {
290     ow_->EndList();
291   }
292 }
293 
RenderDataPiece(StringPiece name,const DataPiece & value)294 void ProtoStreamObjectWriter::AnyWriter::RenderDataPiece(
295     StringPiece name, const DataPiece& value) {
296   // Start an Any only at depth_ 0. Other RenderDataPiece calls with "@type"
297   // should go to the contained ow_ as they indicate nested Anys.
298   if (depth_ == 0 && ow_ == nullptr && name == "@type") {
299     StartAny(value);
300   } else if (ow_ == nullptr) {
301     // Save data before the "@type" field.
302     uninterpreted_events_.push_back(Event(name, value));
303   } else if (depth_ == 0 && is_well_known_type_) {
304     if (name != "value" && !invalid_) {
305       parent_->InvalidValue("Any",
306                             "Expect a \"value\" field for well-known types.");
307       invalid_ = true;
308     }
309     if (well_known_type_render_ == nullptr) {
310       // Only Any and Struct don't have a special type render but both of
311       // them expect a JSON object (i.e., a StartObject() call).
312       if (value.type() != DataPiece::TYPE_NULL && !invalid_) {
313         parent_->InvalidValue("Any", "Expect a JSON object.");
314         invalid_ = true;
315       }
316     } else {
317       ow_->ProtoWriter::StartObject("");
318       Status status = (*well_known_type_render_)(ow_.get(), value);
319       if (!status.ok()) ow_->InvalidValue("Any", status.message());
320       ow_->ProtoWriter::EndObject();
321     }
322   } else {
323     ow_->RenderDataPiece(name, value);
324   }
325 }
326 
StartAny(const DataPiece & value)327 void ProtoStreamObjectWriter::AnyWriter::StartAny(const DataPiece& value) {
328   // Figure out the type url. This is a copy-paste from WriteString but we also
329   // need the value, so we can't just call through to that.
330   if (value.type() == DataPiece::TYPE_STRING) {
331     type_url_ = std::string(value.str());
332   } else {
333     util::StatusOr<std::string> s = value.ToString();
334     if (!s.ok()) {
335       parent_->InvalidValue("String", s.status().message());
336       invalid_ = true;
337       return;
338     }
339     type_url_ = s.value();
340   }
341   // Resolve the type url, and report an error if we failed to resolve it.
342   util::StatusOr<const google::protobuf::Type*> resolved_type =
343       parent_->typeinfo()->ResolveTypeUrl(type_url_);
344   if (!resolved_type.ok()) {
345     parent_->InvalidValue("Any", resolved_type.status().message());
346     invalid_ = true;
347     return;
348   }
349   // At this point, type is never null.
350   const google::protobuf::Type* type = resolved_type.value();
351 
352   well_known_type_render_ = FindTypeRenderer(type_url_);
353   if (well_known_type_render_ != nullptr ||
354       // Explicitly list Any and Struct here because they don't have a
355       // custom renderer.
356       type->name() == kAnyType || type->name() == kStructType) {
357     is_well_known_type_ = true;
358   }
359 
360   // Create our object writer and initialize it with the first StartObject
361   // call.
362   ow_.reset(new ProtoStreamObjectWriter(parent_->typeinfo(), *type, &output_,
363                                         parent_->listener(),
364                                         parent_->options_));
365 
366   // Don't call StartObject() for well-known types yet. Depending on the
367   // type of actual data, we may not need to call StartObject(). For
368   // example:
369   // {
370   //   "@type": "type.googleapis.com/google.protobuf.Value",
371   //   "value": [1, 2, 3],
372   // }
373   // With the above JSON representation, we will only call StartList() on the
374   // contained ow_.
375   if (!is_well_known_type_) {
376     ow_->StartObject("");
377   }
378 
379   // Now we know the proto type and can interpret all data fields we gathered
380   // before the "@type" field.
381   for (int i = 0; i < uninterpreted_events_.size(); ++i) {
382     uninterpreted_events_[i].Replay(this);
383   }
384 }
385 
WriteAny()386 void ProtoStreamObjectWriter::AnyWriter::WriteAny() {
387   if (ow_ == nullptr) {
388     if (uninterpreted_events_.empty()) {
389       // We never got any content, so just return immediately, which is
390       // equivalent to writing an empty Any.
391       return;
392     } else {
393       // There are uninterpreted data, but we never got a "@type" field.
394       if (!invalid_) {
395         parent_->InvalidValue("Any",
396                               StrCat("Missing @type for any field in ",
397                                            parent_->master_type_.name()));
398         invalid_ = true;
399       }
400       return;
401     }
402   }
403   // Render the type_url and value fields directly to the stream.
404   // type_url has tag 1 and value has tag 2.
405   WireFormatLite::WriteString(1, type_url_, parent_->stream());
406   if (!data_.empty()) {
407     WireFormatLite::WriteBytes(2, data_, parent_->stream());
408   }
409 }
410 
Replay(AnyWriter * writer) const411 void ProtoStreamObjectWriter::AnyWriter::Event::Replay(
412     AnyWriter* writer) const {
413   switch (type_) {
414     case START_OBJECT:
415       writer->StartObject(name_);
416       break;
417     case END_OBJECT:
418       writer->EndObject();
419       break;
420     case START_LIST:
421       writer->StartList(name_);
422       break;
423     case END_LIST:
424       writer->EndList();
425       break;
426     case RENDER_DATA_PIECE:
427       writer->RenderDataPiece(name_, value_);
428       break;
429   }
430 }
431 
DeepCopy()432 void ProtoStreamObjectWriter::AnyWriter::Event::DeepCopy() {
433   // DataPiece only contains a string reference. To make sure the referenced
434   // string value stays valid, we make a copy of the string value and update
435   // DataPiece to reference our own copy.
436   if (value_.type() == DataPiece::TYPE_STRING) {
437     StrAppend(&value_storage_, value_.str());
438     value_ = DataPiece(value_storage_, value_.use_strict_base64_decoding());
439   } else if (value_.type() == DataPiece::TYPE_BYTES) {
440     value_storage_ = value_.ToBytes().value();
441     value_ =
442         DataPiece(value_storage_, true, value_.use_strict_base64_decoding());
443   }
444 }
445 
Item(ProtoStreamObjectWriter * enclosing,ItemType item_type,bool is_placeholder,bool is_list)446 ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter* enclosing,
447                                     ItemType item_type, bool is_placeholder,
448                                     bool is_list)
449     : BaseElement(nullptr),
450       ow_(enclosing),
451       any_(),
452       item_type_(item_type),
453       is_placeholder_(is_placeholder),
454       is_list_(is_list) {
455   if (item_type_ == ANY) {
456     any_.reset(new AnyWriter(ow_));
457   }
458   if (item_type == MAP) {
459     map_keys_.reset(new std::unordered_set<std::string>);
460   }
461 }
462 
Item(ProtoStreamObjectWriter::Item * parent,ItemType item_type,bool is_placeholder,bool is_list)463 ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter::Item* parent,
464                                     ItemType item_type, bool is_placeholder,
465                                     bool is_list)
466     : BaseElement(parent),
467       ow_(this->parent()->ow_),
468       any_(),
469       item_type_(item_type),
470       is_placeholder_(is_placeholder),
471       is_list_(is_list) {
472   if (item_type == ANY) {
473     any_.reset(new AnyWriter(ow_));
474   }
475   if (item_type == MAP) {
476     map_keys_.reset(new std::unordered_set<std::string>);
477   }
478 }
479 
InsertMapKeyIfNotPresent(StringPiece map_key)480 bool ProtoStreamObjectWriter::Item::InsertMapKeyIfNotPresent(
481     StringPiece map_key) {
482   return InsertIfNotPresent(map_keys_.get(), std::string(map_key));
483 }
484 
485 
StartObject(StringPiece name)486 ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartObject(
487     StringPiece name) {
488   if (invalid_depth() > 0) {
489     IncrementInvalidDepth();
490     return this;
491   }
492 
493   // Starting the root message. Create the root Item and return.
494   // ANY message type does not need special handling, just set the ItemType
495   // to ANY.
496   if (current_ == nullptr) {
497     ProtoWriter::StartObject(name);
498     current_.reset(new Item(
499         this, master_type_.name() == kAnyType ? Item::ANY : Item::MESSAGE,
500         false, false));
501 
502     // If master type is a special type that needs extra values to be written to
503     // stream, we write those values.
504     if (master_type_.name() == kStructType) {
505       // Struct has a map<string, Value> field called "fields".
506       // https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/struct.proto
507       // "fields": [
508       Push("fields", Item::MAP, true, true);
509       return this;
510     }
511 
512     if (master_type_.name() == kStructValueType) {
513       // We got a StartObject call with google.protobuf.Value field. The only
514       // object within that type is a struct type. So start a struct.
515       //
516       // The struct field in Value type is named "struct_value"
517       // https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/struct.proto
518       // Also start the map field "fields" within the struct.
519       // "struct_value": {
520       //   "fields": [
521       Push("struct_value", Item::MESSAGE, true, false);
522       Push("fields", Item::MAP, true, true);
523       return this;
524     }
525 
526     if (master_type_.name() == kStructListValueType) {
527       InvalidValue(kStructListValueType,
528                    "Cannot start root message with ListValue.");
529     }
530 
531     return this;
532   }
533 
534   // Send all ANY events to AnyWriter.
535   if (current_->IsAny()) {
536     current_->any()->StartObject(name);
537     return this;
538   }
539 
540   // If we are within a map, we render name as keys and send StartObject to the
541   // value field.
542   if (current_->IsMap()) {
543     if (!ValidMapKey(name)) {
544       IncrementInvalidDepth();
545       return this;
546     }
547 
548     // Map is a repeated field of message type with a "key" and a "value" field.
549     // https://developers.google.com/protocol-buffers/docs/proto3?hl=en#maps
550     // message MapFieldEntry {
551     //   key_type key = 1;
552     //   value_type value = 2;
553     // }
554     //
555     // repeated MapFieldEntry map_field = N;
556     //
557     // That means, we render the following element within a list (hence no
558     // name):
559     // { "key": "<name>", "value": {
560     Push("", Item::MESSAGE, false, false);
561     ProtoWriter::RenderDataPiece("key",
562                                  DataPiece(name, use_strict_base64_decoding()));
563     Push("value", IsAny(*Lookup("value")) ? Item::ANY : Item::MESSAGE, true,
564          false);
565 
566     // Make sure we are valid so far after starting map fields.
567     if (invalid_depth() > 0) return this;
568 
569     // If top of stack is g.p.Struct type, start the struct the map field within
570     // it.
571     if (element() != nullptr && IsStruct(*element()->parent_field())) {
572       // Render "fields": [
573       Push("fields", Item::MAP, true, true);
574       return this;
575     }
576 
577     // If top of stack is g.p.Value type, start the Struct within it.
578     if (element() != nullptr && IsStructValue(*element()->parent_field())) {
579       // Render
580       // "struct_value": {
581       //   "fields": [
582       Push("struct_value", Item::MESSAGE, true, false);
583       Push("fields", Item::MAP, true, true);
584     }
585     return this;
586   }
587 
588   const google::protobuf::Field* field = BeginNamed(name, false);
589 
590   if (field == nullptr) return this;
591 
592   // Legacy JSON map is a list of key value pairs. Starts a map entry object.
593   if (options_.use_legacy_json_map_format && name.empty()) {
594     Push(name, IsAny(*field) ? Item::ANY : Item::MESSAGE, false, false);
595     return this;
596   }
597 
598   if (IsMap(*field)) {
599     // Begin a map. A map is triggered by a StartObject() call if the current
600     // field has a map type.
601     // A map type is always repeated, hence set is_list to true.
602     // Render
603     // "<name>": [
604     Push(name, Item::MAP, false, true);
605     return this;
606   }
607 
608   if (options_.disable_implicit_message_list) {
609     // If the incoming object is repeated, the top-level object on stack should
610     // be list. Report an error otherwise.
611     if (IsRepeated(*field) && !current_->is_list()) {
612       IncrementInvalidDepth();
613 
614       if (!options_.suppress_implicit_message_list_error) {
615         InvalidValue(
616             field->name(),
617             "Starting an object in a repeated field but the parent object "
618             "is not a list");
619       }
620       return this;
621     }
622   }
623 
624   if (IsStruct(*field)) {
625     // Start a struct object.
626     // Render
627     // "<name>": {
628     //   "fields": {
629     Push(name, Item::MESSAGE, false, false);
630     Push("fields", Item::MAP, true, true);
631     return this;
632   }
633 
634   if (IsStructValue(*field)) {
635     // We got a StartObject call with google.protobuf.Value field.  The only
636     // object within that type is a struct type. So start a struct.
637     // Render
638     // "<name>": {
639     //   "struct_value": {
640     //     "fields": {
641     Push(name, Item::MESSAGE, false, false);
642     Push("struct_value", Item::MESSAGE, true, false);
643     Push("fields", Item::MAP, true, true);
644     return this;
645   }
646 
647   if (field->kind() != google::protobuf::Field::TYPE_GROUP &&
648       field->kind() != google::protobuf::Field::TYPE_MESSAGE) {
649     IncrementInvalidDepth();
650     if (!options_.suppress_object_to_scalar_error) {
651       InvalidValue(field->name(), "Starting an object on a scalar field");
652     }
653 
654     return this;
655   }
656 
657   // A regular message type. Pass it directly to ProtoWriter.
658   // Render
659   // "<name>": {
660   Push(name, IsAny(*field) ? Item::ANY : Item::MESSAGE, false, false);
661   return this;
662 }
663 
EndObject()664 ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndObject() {
665   if (invalid_depth() > 0) {
666     DecrementInvalidDepth();
667     return this;
668   }
669 
670   if (current_ == nullptr) return this;
671 
672   if (current_->IsAny()) {
673     if (current_->any()->EndObject()) return this;
674   }
675 
676   Pop();
677 
678   return this;
679 }
680 
681 
StartList(StringPiece name)682 ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartList(
683     StringPiece name) {
684   if (invalid_depth() > 0) {
685     IncrementInvalidDepth();
686     return this;
687   }
688 
689   // Since we cannot have a top-level repeated item in protobuf, the only way
690   // this is valid is if we start a special type google.protobuf.ListValue or
691   // google.protobuf.Value.
692   if (current_ == nullptr) {
693     if (!name.empty()) {
694       InvalidName(name, "Root element should not be named.");
695       IncrementInvalidDepth();
696       return this;
697     }
698 
699     // If master type is a special type that needs extra values to be written to
700     // stream, we write those values.
701     if (master_type_.name() == kStructValueType) {
702       // We got a StartList with google.protobuf.Value master type. This means
703       // we have to start the "list_value" within google.protobuf.Value.
704       //
705       // See
706       // https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/struct.proto
707       //
708       // Render
709       // "<name>": {
710       //   "list_value": {
711       //     "values": [  // Start this list.
712       ProtoWriter::StartObject(name);
713       current_.reset(new Item(this, Item::MESSAGE, false, false));
714       Push("list_value", Item::MESSAGE, true, false);
715       Push("values", Item::MESSAGE, true, true);
716       return this;
717     }
718 
719     if (master_type_.name() == kStructListValueType) {
720       // We got a StartList with google.protobuf.ListValue master type. This
721       // means we have to start the "values" within google.protobuf.ListValue.
722       //
723       // Render
724       // "<name>": {
725       //   "values": [  // Start this list.
726       ProtoWriter::StartObject(name);
727       current_.reset(new Item(this, Item::MESSAGE, false, false));
728       Push("values", Item::MESSAGE, true, true);
729       return this;
730     }
731 
732     // Send the event to ProtoWriter so proper errors can be reported.
733     //
734     // Render a regular list:
735     // "<name>": [
736     ProtoWriter::StartList(name);
737     current_.reset(new Item(this, Item::MESSAGE, false, true));
738     return this;
739   }
740 
741   if (current_->IsAny()) {
742     current_->any()->StartList(name);
743     return this;
744   }
745 
746   // If the top of stack is a map, we are starting a list value within a map.
747   // Since map does not allow repeated values, this can only happen when the map
748   // value is of a special type that renders a list in JSON.  These can be one
749   // of 3 cases:
750   // i. We are rendering a list value within google.protobuf.Struct
751   // ii. We are rendering a list value within google.protobuf.Value
752   // iii. We are rendering a list value with type google.protobuf.ListValue.
753   if (current_->IsMap()) {
754     if (!ValidMapKey(name)) {
755       IncrementInvalidDepth();
756       return this;
757     }
758 
759     // Start the repeated map entry object.
760     // Render
761     // { "key": "<name>", "value": {
762     Push("", Item::MESSAGE, false, false);
763     ProtoWriter::RenderDataPiece("key",
764                                  DataPiece(name, use_strict_base64_decoding()));
765     Push("value", Item::MESSAGE, true, false);
766 
767     // Make sure we are valid after pushing all above items.
768     if (invalid_depth() > 0) return this;
769 
770     // case i and ii above. Start "list_value" field within g.p.Value
771     if (element() != nullptr && element()->parent_field() != nullptr) {
772       // Render
773       // "list_value": {
774       //   "values": [  // Start this list
775       if (IsStructValue(*element()->parent_field())) {
776         Push("list_value", Item::MESSAGE, true, false);
777         Push("values", Item::MESSAGE, true, true);
778         return this;
779       }
780 
781       // Render
782       // "values": [
783       if (IsStructListValue(*element()->parent_field())) {
784         // case iii above. Bind directly to g.p.ListValue
785         Push("values", Item::MESSAGE, true, true);
786         return this;
787       }
788     }
789 
790     // Report an error.
791     InvalidValue("Map", StrCat("Cannot have repeated items ('", name,
792                                      "') within a map."));
793     return this;
794   }
795 
796   // When name is empty and stack is not empty, we are rendering an item within
797   // a list.
798   if (name.empty()) {
799     if (element() != nullptr && element()->parent_field() != nullptr) {
800       if (IsStructValue(*element()->parent_field())) {
801         // Since it is g.p.Value, we bind directly to the list_value.
802         // Render
803         // {  // g.p.Value item within the list
804         //   "list_value": {
805         //     "values": [
806         Push("", Item::MESSAGE, false, false);
807         Push("list_value", Item::MESSAGE, true, false);
808         Push("values", Item::MESSAGE, true, true);
809         return this;
810       }
811 
812       if (IsStructListValue(*element()->parent_field())) {
813         // Since it is g.p.ListValue, we bind to it directly.
814         // Render
815         // {  // g.p.ListValue item within the list
816         //   "values": [
817         Push("", Item::MESSAGE, false, false);
818         Push("values", Item::MESSAGE, true, true);
819         return this;
820       }
821     }
822 
823     // Pass the event to underlying ProtoWriter.
824     Push(name, Item::MESSAGE, false, true);
825     return this;
826   }
827 
828   // name is not empty
829   const google::protobuf::Field* field = Lookup(name);
830 
831   if (field == nullptr) {
832     IncrementInvalidDepth();
833     return this;
834   }
835 
836   if (IsStructValue(*field)) {
837     // If g.p.Value is repeated, start that list. Otherwise, start the
838     // "list_value" within it.
839     if (IsRepeated(*field)) {
840       // Render it just like a regular repeated field.
841       // "<name>": [
842       Push(name, Item::MESSAGE, false, true);
843       return this;
844     }
845 
846     // Start the "list_value" field.
847     // Render
848     // "<name>": {
849     //   "list_value": {
850     //     "values": [
851     Push(name, Item::MESSAGE, false, false);
852     Push("list_value", Item::MESSAGE, true, false);
853     Push("values", Item::MESSAGE, true, true);
854     return this;
855   }
856 
857   if (IsStructListValue(*field)) {
858     // If g.p.ListValue is repeated, start that list. Otherwise, start the
859     // "values" within it.
860     if (IsRepeated(*field)) {
861       // Render it just like a regular repeated field.
862       // "<name>": [
863       Push(name, Item::MESSAGE, false, true);
864       return this;
865     }
866 
867     // Start the "values" field within g.p.ListValue.
868     // Render
869     // "<name>": {
870     //   "values": [
871     Push(name, Item::MESSAGE, false, false);
872     Push("values", Item::MESSAGE, true, true);
873     return this;
874   }
875 
876   // If we are here, the field should be repeated. Report an error otherwise.
877   if (!IsRepeated(*field)) {
878     IncrementInvalidDepth();
879     InvalidName(name, "Proto field is not repeating, cannot start list.");
880     return this;
881   }
882 
883   if (IsMap(*field)) {
884     if (options_.use_legacy_json_map_format) {
885       Push(name, Item::MESSAGE, false, true);
886       return this;
887     }
888     InvalidValue("Map", StrCat("Cannot bind a list to map for field '",
889                                      name, "'."));
890     IncrementInvalidDepth();
891     return this;
892   }
893 
894   // Pass the event to ProtoWriter.
895   // Render
896   // "<name>": [
897   Push(name, Item::MESSAGE, false, true);
898   return this;
899 }
900 
EndList()901 ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndList() {
902   if (invalid_depth() > 0) {
903     DecrementInvalidDepth();
904     return this;
905   }
906 
907   if (current_ == nullptr) return this;
908 
909   if (current_->IsAny()) {
910     current_->any()->EndList();
911     return this;
912   }
913 
914   Pop();
915   return this;
916 }
917 
RenderStructValue(ProtoStreamObjectWriter * ow,const DataPiece & data)918 Status ProtoStreamObjectWriter::RenderStructValue(ProtoStreamObjectWriter* ow,
919                                                   const DataPiece& data) {
920   std::string struct_field_name;
921   switch (data.type()) {
922     case DataPiece::TYPE_INT32: {
923       if (ow->options_.struct_integers_as_strings) {
924         util::StatusOr<int32_t> int_value = data.ToInt32();
925         if (int_value.ok()) {
926           ow->ProtoWriter::RenderDataPiece(
927               "string_value",
928               DataPiece(SimpleDtoa(int_value.value()), true));
929           return Status();
930         }
931       }
932       struct_field_name = "number_value";
933       break;
934     }
935     case DataPiece::TYPE_UINT32: {
936       if (ow->options_.struct_integers_as_strings) {
937         util::StatusOr<uint32_t> int_value = data.ToUint32();
938         if (int_value.ok()) {
939           ow->ProtoWriter::RenderDataPiece(
940               "string_value",
941               DataPiece(SimpleDtoa(int_value.value()), true));
942           return Status();
943         }
944       }
945       struct_field_name = "number_value";
946       break;
947     }
948     case DataPiece::TYPE_INT64: {
949       // If the option to treat integers as strings is set, then render them as
950       // strings. Otherwise, fallback to rendering them as double.
951       if (ow->options_.struct_integers_as_strings) {
952         util::StatusOr<int64_t> int_value = data.ToInt64();
953         if (int_value.ok()) {
954           ow->ProtoWriter::RenderDataPiece(
955               "string_value", DataPiece(StrCat(int_value.value()), true));
956           return Status();
957         }
958       }
959       struct_field_name = "number_value";
960       break;
961     }
962     case DataPiece::TYPE_UINT64: {
963       // If the option to treat integers as strings is set, then render them as
964       // strings. Otherwise, fallback to rendering them as double.
965       if (ow->options_.struct_integers_as_strings) {
966         util::StatusOr<uint64_t> int_value = data.ToUint64();
967         if (int_value.ok()) {
968           ow->ProtoWriter::RenderDataPiece(
969               "string_value", DataPiece(StrCat(int_value.value()), true));
970           return Status();
971         }
972       }
973       struct_field_name = "number_value";
974       break;
975     }
976     case DataPiece::TYPE_FLOAT: {
977       if (ow->options_.struct_integers_as_strings) {
978         util::StatusOr<float> float_value = data.ToFloat();
979         if (float_value.ok()) {
980           ow->ProtoWriter::RenderDataPiece(
981               "string_value",
982               DataPiece(SimpleDtoa(float_value.value()), true));
983           return Status();
984         }
985       }
986       struct_field_name = "number_value";
987       break;
988     }
989     case DataPiece::TYPE_DOUBLE: {
990       if (ow->options_.struct_integers_as_strings) {
991         util::StatusOr<double> double_value = data.ToDouble();
992         if (double_value.ok()) {
993           ow->ProtoWriter::RenderDataPiece(
994               "string_value",
995               DataPiece(SimpleDtoa(double_value.value()), true));
996           return Status();
997         }
998       }
999       struct_field_name = "number_value";
1000       break;
1001     }
1002     case DataPiece::TYPE_STRING: {
1003       struct_field_name = "string_value";
1004       break;
1005     }
1006     case DataPiece::TYPE_BOOL: {
1007       struct_field_name = "bool_value";
1008       break;
1009     }
1010     case DataPiece::TYPE_NULL: {
1011       struct_field_name = "null_value";
1012       break;
1013     }
1014     default: {
1015       return util::InvalidArgumentError(
1016           "Invalid struct data type. Only number, string, boolean or  null "
1017           "values are supported.");
1018     }
1019   }
1020   ow->ProtoWriter::RenderDataPiece(struct_field_name, data);
1021   return Status();
1022 }
1023 
RenderTimestamp(ProtoStreamObjectWriter * ow,const DataPiece & data)1024 Status ProtoStreamObjectWriter::RenderTimestamp(ProtoStreamObjectWriter* ow,
1025                                                 const DataPiece& data) {
1026   if (data.type() == DataPiece::TYPE_NULL) return Status();
1027   if (data.type() != DataPiece::TYPE_STRING) {
1028     return util::InvalidArgumentError(
1029         StrCat("Invalid data type for timestamp, value is ",
1030                      data.ValueAsStringOrDefault("")));
1031   }
1032 
1033   StringPiece value(data.str());
1034 
1035   int64_t seconds;
1036   int32_t nanos;
1037   if (!::google::protobuf::internal::ParseTime(value.ToString(), &seconds,
1038                                                &nanos)) {
1039     return util::InvalidArgumentError(StrCat("Invalid time format: ", value));
1040   }
1041 
1042 
1043   ow->ProtoWriter::RenderDataPiece("seconds", DataPiece(seconds));
1044   ow->ProtoWriter::RenderDataPiece("nanos", DataPiece(nanos));
1045   return Status();
1046 }
1047 
RenderOneFieldPath(ProtoStreamObjectWriter * ow,StringPiece path)1048 static inline util::Status RenderOneFieldPath(ProtoStreamObjectWriter* ow,
1049                                               StringPiece path) {
1050   ow->ProtoWriter::RenderDataPiece(
1051       "paths", DataPiece(ConvertFieldMaskPath(path, &ToSnakeCase), true));
1052   return Status();
1053 }
1054 
RenderFieldMask(ProtoStreamObjectWriter * ow,const DataPiece & data)1055 Status ProtoStreamObjectWriter::RenderFieldMask(ProtoStreamObjectWriter* ow,
1056                                                 const DataPiece& data) {
1057   if (data.type() == DataPiece::TYPE_NULL) return Status();
1058   if (data.type() != DataPiece::TYPE_STRING) {
1059     return util::InvalidArgumentError(
1060         StrCat("Invalid data type for field mask, value is ",
1061                      data.ValueAsStringOrDefault("")));
1062   }
1063 
1064   // TODO(tsun): figure out how to do proto descriptor based snake case
1065   // conversions as much as possible. Because ToSnakeCase sometimes returns the
1066   // wrong value.
1067   return DecodeCompactFieldMaskPaths(data.str(),
1068                                      std::bind(&RenderOneFieldPath, ow, _1));
1069 }
1070 
RenderDuration(ProtoStreamObjectWriter * ow,const DataPiece & data)1071 Status ProtoStreamObjectWriter::RenderDuration(ProtoStreamObjectWriter* ow,
1072                                                const DataPiece& data) {
1073   if (data.type() == DataPiece::TYPE_NULL) return Status();
1074   if (data.type() != DataPiece::TYPE_STRING) {
1075     return util::InvalidArgumentError(
1076         StrCat("Invalid data type for duration, value is ",
1077                      data.ValueAsStringOrDefault("")));
1078   }
1079 
1080   StringPiece value(data.str());
1081 
1082   if (!HasSuffixString(value, "s")) {
1083     return util::InvalidArgumentError(
1084         "Illegal duration format; duration must end with 's'");
1085   }
1086   value = value.substr(0, value.size() - 1);
1087   int sign = 1;
1088   if (HasPrefixString(value, "-")) {
1089     sign = -1;
1090     value = value.substr(1);
1091   }
1092 
1093   StringPiece s_secs, s_nanos;
1094   SplitSecondsAndNanos(value, &s_secs, &s_nanos);
1095   uint64_t unsigned_seconds;
1096   if (!safe_strtou64(s_secs, &unsigned_seconds)) {
1097     return util::InvalidArgumentError(
1098         "Invalid duration format, failed to parse seconds");
1099   }
1100 
1101   int32_t nanos = 0;
1102   Status nanos_status = GetNanosFromStringPiece(
1103       s_nanos, "Invalid duration format, failed to parse nano seconds",
1104       "Duration value exceeds limits", &nanos);
1105   if (!nanos_status.ok()) {
1106     return nanos_status;
1107   }
1108   nanos = sign * nanos;
1109 
1110   int64_t seconds = sign * unsigned_seconds;
1111   if (seconds > kDurationMaxSeconds || seconds < kDurationMinSeconds ||
1112       nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) {
1113     return util::InvalidArgumentError("Duration value exceeds limits");
1114   }
1115 
1116   ow->ProtoWriter::RenderDataPiece("seconds", DataPiece(seconds));
1117   ow->ProtoWriter::RenderDataPiece("nanos", DataPiece(nanos));
1118   return Status();
1119 }
1120 
RenderWrapperType(ProtoStreamObjectWriter * ow,const DataPiece & data)1121 Status ProtoStreamObjectWriter::RenderWrapperType(ProtoStreamObjectWriter* ow,
1122                                                   const DataPiece& data) {
1123   if (data.type() == DataPiece::TYPE_NULL) return Status();
1124   ow->ProtoWriter::RenderDataPiece("value", data);
1125   return Status();
1126 }
1127 
RenderDataPiece(StringPiece name,const DataPiece & data)1128 ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece(
1129     StringPiece name, const DataPiece& data) {
1130   Status status;
1131   if (invalid_depth() > 0) return this;
1132 
1133   if (current_ == nullptr) {
1134     const TypeRenderer* type_renderer =
1135         FindTypeRenderer(GetFullTypeWithUrl(master_type_.name()));
1136     if (type_renderer == nullptr) {
1137       InvalidName(name, "Root element must be a message.");
1138       return this;
1139     }
1140     // Render the special type.
1141     // "<name>": {
1142     //   ... Render special type ...
1143     // }
1144     ProtoWriter::StartObject(name);
1145     status = (*type_renderer)(this, data);
1146     if (!status.ok()) {
1147       InvalidValue(master_type_.name(),
1148                    StrCat("Field '", name, "', ", status.message()));
1149     }
1150     ProtoWriter::EndObject();
1151     return this;
1152   }
1153 
1154   if (current_->IsAny()) {
1155     current_->any()->RenderDataPiece(name, data);
1156     return this;
1157   }
1158 
1159   const google::protobuf::Field* field = nullptr;
1160   if (current_->IsMap()) {
1161     if (!ValidMapKey(name)) return this;
1162 
1163     field = Lookup("value");
1164     if (field == nullptr) {
1165       GOOGLE_LOG(DFATAL) << "Map does not have a value field.";
1166       return this;
1167     }
1168 
1169     if (options_.ignore_null_value_map_entry) {
1170       // If we are rendering explicit null values and the backend proto field is
1171       // not of the google.protobuf.NullType type, interpret null as absence.
1172       if (data.type() == DataPiece::TYPE_NULL &&
1173           field->type_url() != kStructNullValueTypeUrl) {
1174         return this;
1175       }
1176     }
1177 
1178     // Render an item in repeated map list.
1179     // { "key": "<name>", "value":
1180     Push("", Item::MESSAGE, false, false);
1181     ProtoWriter::RenderDataPiece("key",
1182                                  DataPiece(name, use_strict_base64_decoding()));
1183 
1184     const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url());
1185     if (type_renderer != nullptr) {
1186       // Map's value type is a special type. Render it like a message:
1187       // "value": {
1188       //   ... Render special type ...
1189       // }
1190       Push("value", Item::MESSAGE, true, false);
1191       status = (*type_renderer)(this, data);
1192       if (!status.ok()) {
1193         InvalidValue(field->type_url(),
1194                      StrCat("Field '", name, "', ", status.message()));
1195       }
1196       Pop();
1197       return this;
1198     }
1199 
1200     // If we are rendering explicit null values and the backend proto field is
1201     // not of the google.protobuf.NullType type, we do nothing.
1202     if (data.type() == DataPiece::TYPE_NULL &&
1203         field->type_url() != kStructNullValueTypeUrl) {
1204       Pop();
1205       return this;
1206     }
1207 
1208     // Render the map value as a primitive type.
1209     ProtoWriter::RenderDataPiece("value", data);
1210     Pop();
1211     return this;
1212   }
1213 
1214   field = Lookup(name);
1215   if (field == nullptr) return this;
1216 
1217   // Check if the field is of special type. Render it accordingly if so.
1218   const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url());
1219   if (type_renderer != nullptr) {
1220     // Pass through null value only for google.protobuf.Value. For other
1221     // types we ignore null value just like for regular field types.
1222     if (data.type() != DataPiece::TYPE_NULL ||
1223         field->type_url() == kStructValueTypeUrl) {
1224       Push(name, Item::MESSAGE, false, false);
1225       status = (*type_renderer)(this, data);
1226       if (!status.ok()) {
1227         InvalidValue(field->type_url(),
1228                      StrCat("Field '", name, "', ", status.message()));
1229       }
1230       Pop();
1231     }
1232     return this;
1233   }
1234 
1235   // If we are rendering explicit null values and the backend proto field is
1236   // not of the google.protobuf.NullType type, we do nothing.
1237   if (data.type() == DataPiece::TYPE_NULL &&
1238       field->type_url() != kStructNullValueTypeUrl) {
1239     return this;
1240   }
1241 
1242   if (IsRepeated(*field) && !current_->is_list()) {
1243     if (options_.disable_implicit_scalar_list) {
1244       if (!options_.suppress_implicit_scalar_list_error) {
1245         InvalidValue(
1246             field->name(),
1247             "Starting an primitive in a repeated field but the parent field "
1248             "is not a list");
1249       }
1250 
1251       return this;
1252     }
1253   }
1254 
1255   ProtoWriter::RenderDataPiece(name, data);
1256   return this;
1257 }
1258 
1259 // Map of functions that are responsible for rendering well known type
1260 // represented by the key.
1261 std::unordered_map<std::string, ProtoStreamObjectWriter::TypeRenderer>*
1262     ProtoStreamObjectWriter::renderers_ = nullptr;
1263 PROTOBUF_NAMESPACE_ID::internal::once_flag writer_renderers_init_;
1264 
InitRendererMap()1265 void ProtoStreamObjectWriter::InitRendererMap() {
1266   renderers_ = new std::unordered_map<std::string,
1267                                       ProtoStreamObjectWriter::TypeRenderer>();
1268   (*renderers_)["type.googleapis.com/google.protobuf.Timestamp"] =
1269       &ProtoStreamObjectWriter::RenderTimestamp;
1270   (*renderers_)["type.googleapis.com/google.protobuf.Duration"] =
1271       &ProtoStreamObjectWriter::RenderDuration;
1272   (*renderers_)["type.googleapis.com/google.protobuf.FieldMask"] =
1273       &ProtoStreamObjectWriter::RenderFieldMask;
1274   (*renderers_)["type.googleapis.com/google.protobuf.Double"] =
1275       &ProtoStreamObjectWriter::RenderWrapperType;
1276   (*renderers_)["type.googleapis.com/google.protobuf.Float"] =
1277       &ProtoStreamObjectWriter::RenderWrapperType;
1278   (*renderers_)["type.googleapis.com/google.protobuf.Int64"] =
1279       &ProtoStreamObjectWriter::RenderWrapperType;
1280   (*renderers_)["type.googleapis.com/google.protobuf.UInt64"] =
1281       &ProtoStreamObjectWriter::RenderWrapperType;
1282   (*renderers_)["type.googleapis.com/google.protobuf.Int32"] =
1283       &ProtoStreamObjectWriter::RenderWrapperType;
1284   (*renderers_)["type.googleapis.com/google.protobuf.UInt32"] =
1285       &ProtoStreamObjectWriter::RenderWrapperType;
1286   (*renderers_)["type.googleapis.com/google.protobuf.Bool"] =
1287       &ProtoStreamObjectWriter::RenderWrapperType;
1288   (*renderers_)["type.googleapis.com/google.protobuf.String"] =
1289       &ProtoStreamObjectWriter::RenderWrapperType;
1290   (*renderers_)["type.googleapis.com/google.protobuf.Bytes"] =
1291       &ProtoStreamObjectWriter::RenderWrapperType;
1292   (*renderers_)["type.googleapis.com/google.protobuf.DoubleValue"] =
1293       &ProtoStreamObjectWriter::RenderWrapperType;
1294   (*renderers_)["type.googleapis.com/google.protobuf.FloatValue"] =
1295       &ProtoStreamObjectWriter::RenderWrapperType;
1296   (*renderers_)["type.googleapis.com/google.protobuf.Int64Value"] =
1297       &ProtoStreamObjectWriter::RenderWrapperType;
1298   (*renderers_)["type.googleapis.com/google.protobuf.UInt64Value"] =
1299       &ProtoStreamObjectWriter::RenderWrapperType;
1300   (*renderers_)["type.googleapis.com/google.protobuf.Int32Value"] =
1301       &ProtoStreamObjectWriter::RenderWrapperType;
1302   (*renderers_)["type.googleapis.com/google.protobuf.UInt32Value"] =
1303       &ProtoStreamObjectWriter::RenderWrapperType;
1304   (*renderers_)["type.googleapis.com/google.protobuf.BoolValue"] =
1305       &ProtoStreamObjectWriter::RenderWrapperType;
1306   (*renderers_)["type.googleapis.com/google.protobuf.StringValue"] =
1307       &ProtoStreamObjectWriter::RenderWrapperType;
1308   (*renderers_)["type.googleapis.com/google.protobuf.BytesValue"] =
1309       &ProtoStreamObjectWriter::RenderWrapperType;
1310   (*renderers_)["type.googleapis.com/google.protobuf.Value"] =
1311       &ProtoStreamObjectWriter::RenderStructValue;
1312   ::google::protobuf::internal::OnShutdown(&DeleteRendererMap);
1313 }
1314 
DeleteRendererMap()1315 void ProtoStreamObjectWriter::DeleteRendererMap() {
1316   delete ProtoStreamObjectWriter::renderers_;
1317   renderers_ = nullptr;
1318 }
1319 
1320 ProtoStreamObjectWriter::TypeRenderer*
FindTypeRenderer(const std::string & type_url)1321 ProtoStreamObjectWriter::FindTypeRenderer(const std::string& type_url) {
1322   PROTOBUF_NAMESPACE_ID::internal::call_once(writer_renderers_init_,
1323                                              InitRendererMap);
1324   return FindOrNull(*renderers_, type_url);
1325 }
1326 
ValidMapKey(StringPiece unnormalized_name)1327 bool ProtoStreamObjectWriter::ValidMapKey(StringPiece unnormalized_name) {
1328   if (current_ == nullptr) return true;
1329 
1330   if (!current_->InsertMapKeyIfNotPresent(unnormalized_name)) {
1331     listener()->InvalidName(
1332         location(), unnormalized_name,
1333         StrCat("Repeated map key: '", unnormalized_name,
1334                      "' is already set."));
1335     return false;
1336   }
1337 
1338   return true;
1339 }
1340 
Push(StringPiece name,Item::ItemType item_type,bool is_placeholder,bool is_list)1341 void ProtoStreamObjectWriter::Push(
1342     StringPiece name, Item::ItemType item_type, bool is_placeholder,
1343     bool is_list) {
1344   is_list ? ProtoWriter::StartList(name) : ProtoWriter::StartObject(name);
1345 
1346   // invalid_depth == 0 means it is a successful StartObject or StartList.
1347   if (invalid_depth() == 0)
1348     current_.reset(
1349         new Item(current_.release(), item_type, is_placeholder, is_list));
1350 }
1351 
Pop()1352 void ProtoStreamObjectWriter::Pop() {
1353   // Pop all placeholder items sending StartObject or StartList events to
1354   // ProtoWriter according to is_list value.
1355   while (current_ != nullptr && current_->is_placeholder()) {
1356     PopOneElement();
1357   }
1358   if (current_ != nullptr) {
1359     PopOneElement();
1360   }
1361 }
1362 
PopOneElement()1363 void ProtoStreamObjectWriter::PopOneElement() {
1364   current_->is_list() ? ProtoWriter::EndList() : ProtoWriter::EndObject();
1365   current_.reset(current_->pop<Item>());
1366 }
1367 
IsMap(const google::protobuf::Field & field)1368 bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) {
1369   if (field.type_url().empty() ||
1370       field.kind() != google::protobuf::Field::TYPE_MESSAGE ||
1371       field.cardinality() != google::protobuf::Field::CARDINALITY_REPEATED) {
1372     return false;
1373   }
1374   const google::protobuf::Type* field_type =
1375       typeinfo()->GetTypeByTypeUrl(field.type_url());
1376 
1377   return converter::IsMap(field, *field_type);
1378 }
1379 
IsAny(const google::protobuf::Field & field)1380 bool ProtoStreamObjectWriter::IsAny(const google::protobuf::Field& field) {
1381   return GetTypeWithoutUrl(field.type_url()) == kAnyType;
1382 }
1383 
IsStruct(const google::protobuf::Field & field)1384 bool ProtoStreamObjectWriter::IsStruct(const google::protobuf::Field& field) {
1385   return GetTypeWithoutUrl(field.type_url()) == kStructType;
1386 }
1387 
IsStructValue(const google::protobuf::Field & field)1388 bool ProtoStreamObjectWriter::IsStructValue(
1389     const google::protobuf::Field& field) {
1390   return GetTypeWithoutUrl(field.type_url()) == kStructValueType;
1391 }
1392 
IsStructListValue(const google::protobuf::Field & field)1393 bool ProtoStreamObjectWriter::IsStructListValue(
1394     const google::protobuf::Field& field) {
1395   return GetTypeWithoutUrl(field.type_url()) == kStructListValueType;
1396 }
1397 
1398 }  // namespace converter
1399 }  // namespace util
1400 }  // namespace protobuf
1401 }  // namespace google
1402