• 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 <functional>
34 #include <stack>
35 #include <unordered_map>
36 #include <unordered_set>
37 
38 #include <google/protobuf/stubs/once.h>
39 #include <google/protobuf/wire_format_lite.h>
40 #include <google/protobuf/util/internal/field_mask_utility.h>
41 #include <google/protobuf/util/internal/object_location_tracker.h>
42 #include <google/protobuf/util/internal/constants.h>
43 #include <google/protobuf/util/internal/utility.h>
44 #include <google/protobuf/stubs/strutil.h>
45 #include <google/protobuf/stubs/status.h>
46 #include <google/protobuf/stubs/time.h>
47 #include <google/protobuf/stubs/map_util.h>
48 #include <google/protobuf/stubs/statusor.h>
49 
50 
51 #include <google/protobuf/port_def.inc>
52 
53 namespace google {
54 namespace protobuf {
55 namespace util {
56 namespace converter {
57 
58 using ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite;
59 using std::placeholders::_1;
60 using util::Status;
61 using util::error::INVALID_ARGUMENT;
62 
63 
ProtoStreamObjectWriter(TypeResolver * type_resolver,const google::protobuf::Type & type,strings::ByteSink * output,ErrorListener * listener,const ProtoStreamObjectWriter::Options & options)64 ProtoStreamObjectWriter::ProtoStreamObjectWriter(
65     TypeResolver* type_resolver, const google::protobuf::Type& type,
66     strings::ByteSink* output, ErrorListener* listener,
67     const ProtoStreamObjectWriter::Options& options)
68     : ProtoWriter(type_resolver, type, output, listener),
69       master_type_(type),
70       current_(nullptr),
71       options_(options) {
72   set_ignore_unknown_fields(options_.ignore_unknown_fields);
73   set_ignore_unknown_enum_values(options_.ignore_unknown_enum_values);
74   set_use_lower_camel_for_enums(options_.use_lower_camel_for_enums);
75   set_case_insensitive_enum_parsing(options_.case_insensitive_enum_parsing);
76   set_use_json_name_in_missing_fields(options.use_json_name_in_missing_fields);
77 }
78 
ProtoStreamObjectWriter(const TypeInfo * typeinfo,const google::protobuf::Type & type,strings::ByteSink * output,ErrorListener * listener,const ProtoStreamObjectWriter::Options & options)79 ProtoStreamObjectWriter::ProtoStreamObjectWriter(
80     const TypeInfo* typeinfo, const google::protobuf::Type& type,
81     strings::ByteSink* output, ErrorListener* listener,
82     const ProtoStreamObjectWriter::Options& options)
83     : ProtoWriter(typeinfo, type, output, listener),
84       master_type_(type),
85       current_(nullptr),
86       options_(options) {
87   set_ignore_unknown_fields(options_.ignore_unknown_fields);
88   set_use_lower_camel_for_enums(options.use_lower_camel_for_enums);
89   set_case_insensitive_enum_parsing(options_.case_insensitive_enum_parsing);
90   set_use_json_name_in_missing_fields(options.use_json_name_in_missing_fields);
91 }
92 
ProtoStreamObjectWriter(const TypeInfo * typeinfo,const google::protobuf::Type & type,strings::ByteSink * output,ErrorListener * listener)93 ProtoStreamObjectWriter::ProtoStreamObjectWriter(
94     const TypeInfo* typeinfo, const google::protobuf::Type& type,
95     strings::ByteSink* output, ErrorListener* listener)
96     : ProtoWriter(typeinfo, type, output, listener),
97       master_type_(type),
98       current_(nullptr),
99       options_(ProtoStreamObjectWriter::Options::Defaults()) {}
100 
~ProtoStreamObjectWriter()101 ProtoStreamObjectWriter::~ProtoStreamObjectWriter() {
102   if (current_ == nullptr) return;
103   // Cleanup explicitly in order to avoid destructor stack overflow when input
104   // is deeply nested.
105   // Cast to BaseElement to avoid doing additional checks (like missing fields)
106   // during pop().
107   std::unique_ptr<BaseElement> element(
108       static_cast<BaseElement*>(current_.get())->pop<BaseElement>());
109   while (element != nullptr) {
110     element.reset(element->pop<BaseElement>());
111   }
112 }
113 
114 namespace {
115 // Utility method to split a string representation of Timestamp or Duration and
116 // return the parts.
SplitSecondsAndNanos(StringPiece input,StringPiece * seconds,StringPiece * nanos)117 void SplitSecondsAndNanos(StringPiece input, StringPiece* seconds,
118                           StringPiece* nanos) {
119   size_t idx = input.rfind('.');
120   if (idx != std::string::npos) {
121     *seconds = input.substr(0, idx);
122     *nanos = input.substr(idx + 1);
123   } else {
124     *seconds = input;
125     *nanos = StringPiece();
126   }
127 }
128 
GetNanosFromStringPiece(StringPiece s_nanos,const char * parse_failure_message,const char * exceeded_limit_message,int32 * nanos)129 Status GetNanosFromStringPiece(StringPiece s_nanos,
130                                const char* parse_failure_message,
131                                const char* exceeded_limit_message,
132                                int32* nanos) {
133   *nanos = 0;
134 
135   // Count the number of leading 0s and consume them.
136   int num_leading_zeros = 0;
137   while (s_nanos.Consume("0")) {
138     num_leading_zeros++;
139   }
140   int32 i_nanos = 0;
141   // 's_nanos' contains fractional seconds -- i.e. 'nanos' is equal to
142   // "0." + s_nanos.ToString() seconds. An int32 is used for the
143   // conversion to 'nanos', rather than a double, so that there is no
144   // loss of precision.
145   if (!s_nanos.empty() && !safe_strto32(s_nanos, &i_nanos)) {
146     return Status(util::error::INVALID_ARGUMENT, parse_failure_message);
147   }
148   if (i_nanos > kNanosPerSecond || i_nanos < 0) {
149     return Status(util::error::INVALID_ARGUMENT, exceeded_limit_message);
150   }
151   // s_nanos should only have digits. No whitespace.
152   if (s_nanos.find_first_not_of("0123456789") != StringPiece::npos) {
153     return Status(util::error::INVALID_ARGUMENT, parse_failure_message);
154   }
155 
156   if (i_nanos > 0) {
157     // 'scale' is the number of digits to the right of the decimal
158     // point in "0." + s_nanos.ToString()
159     int32 scale = num_leading_zeros + s_nanos.size();
160     // 'conversion' converts i_nanos into nanoseconds.
161     // conversion = kNanosPerSecond / static_cast<int32>(std::pow(10, scale))
162     // For efficiency, we precompute the conversion factor.
163     int32 conversion = 0;
164     switch (scale) {
165       case 1:
166         conversion = 100000000;
167         break;
168       case 2:
169         conversion = 10000000;
170         break;
171       case 3:
172         conversion = 1000000;
173         break;
174       case 4:
175         conversion = 100000;
176         break;
177       case 5:
178         conversion = 10000;
179         break;
180       case 6:
181         conversion = 1000;
182         break;
183       case 7:
184         conversion = 100;
185         break;
186       case 8:
187         conversion = 10;
188         break;
189       case 9:
190         conversion = 1;
191         break;
192       default:
193         return Status(util::error::INVALID_ARGUMENT,
194                       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().ValueOrDie();
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/master/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/master/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/master/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> 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> 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> 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> 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 Status(util::error::INVALID_ARGUMENT,
1016                     "Invalid struct data type. Only number, string, boolean or "
1017                     "null 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 Status(util::error::INVALID_ARGUMENT,
1029                   StrCat("Invalid data type for timestamp, value is ",
1030                                data.ValueAsStringOrDefault("")));
1031   }
1032 
1033   StringPiece value(data.str());
1034 
1035   int64 seconds;
1036   int32 nanos;
1037   if (!::google::protobuf::internal::ParseTime(value.ToString(), &seconds,
1038                                                &nanos)) {
1039     return Status(INVALID_ARGUMENT, 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 Status(util::error::INVALID_ARGUMENT,
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 Status(util::error::INVALID_ARGUMENT,
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 Status(util::error::INVALID_ARGUMENT,
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 unsigned_seconds;
1096   if (!safe_strtou64(s_secs, &unsigned_seconds)) {
1097     return Status(util::error::INVALID_ARGUMENT,
1098                   "Invalid duration format, failed to parse seconds");
1099   }
1100 
1101   int32 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 seconds = sign * unsigned_seconds;
1111   if (seconds > kDurationMaxSeconds || seconds < kDurationMinSeconds ||
1112       nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) {
1113     return Status(util::error::INVALID_ARGUMENT,
1114                   "Duration value exceeds limits");
1115   }
1116 
1117   ow->ProtoWriter::RenderDataPiece("seconds", DataPiece(seconds));
1118   ow->ProtoWriter::RenderDataPiece("nanos", DataPiece(nanos));
1119   return Status();
1120 }
1121 
RenderWrapperType(ProtoStreamObjectWriter * ow,const DataPiece & data)1122 Status ProtoStreamObjectWriter::RenderWrapperType(ProtoStreamObjectWriter* ow,
1123                                                   const DataPiece& data) {
1124   if (data.type() == DataPiece::TYPE_NULL) return Status();
1125   ow->ProtoWriter::RenderDataPiece("value", data);
1126   return Status();
1127 }
1128 
RenderDataPiece(StringPiece name,const DataPiece & data)1129 ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece(
1130     StringPiece name, const DataPiece& data) {
1131   Status status;
1132   if (invalid_depth() > 0) return this;
1133 
1134   if (current_ == nullptr) {
1135     const TypeRenderer* type_renderer =
1136         FindTypeRenderer(GetFullTypeWithUrl(master_type_.name()));
1137     if (type_renderer == nullptr) {
1138       InvalidName(name, "Root element must be a message.");
1139       return this;
1140     }
1141     // Render the special type.
1142     // "<name>": {
1143     //   ... Render special type ...
1144     // }
1145     ProtoWriter::StartObject(name);
1146     status = (*type_renderer)(this, data);
1147     if (!status.ok()) {
1148       InvalidValue(master_type_.name(),
1149                    StrCat("Field '", name, "', ", status.message()));
1150     }
1151     ProtoWriter::EndObject();
1152     return this;
1153   }
1154 
1155   if (current_->IsAny()) {
1156     current_->any()->RenderDataPiece(name, data);
1157     return this;
1158   }
1159 
1160   const google::protobuf::Field* field = nullptr;
1161   if (current_->IsMap()) {
1162     if (!ValidMapKey(name)) return this;
1163 
1164     field = Lookup("value");
1165     if (field == nullptr) {
1166       GOOGLE_LOG(DFATAL) << "Map does not have a value field.";
1167       return this;
1168     }
1169 
1170     if (options_.ignore_null_value_map_entry) {
1171       // If we are rendering explicit null values and the backend proto field is
1172       // not of the google.protobuf.NullType type, interpret null as absence.
1173       if (data.type() == DataPiece::TYPE_NULL &&
1174           field->type_url() != kStructNullValueTypeUrl) {
1175         return this;
1176       }
1177     }
1178 
1179     // Render an item in repeated map list.
1180     // { "key": "<name>", "value":
1181     Push("", Item::MESSAGE, false, false);
1182     ProtoWriter::RenderDataPiece("key",
1183                                  DataPiece(name, use_strict_base64_decoding()));
1184 
1185     const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url());
1186     if (type_renderer != nullptr) {
1187       // Map's value type is a special type. Render it like a message:
1188       // "value": {
1189       //   ... Render special type ...
1190       // }
1191       Push("value", Item::MESSAGE, true, false);
1192       status = (*type_renderer)(this, data);
1193       if (!status.ok()) {
1194         InvalidValue(field->type_url(),
1195                      StrCat("Field '", name, "', ", status.message()));
1196       }
1197       Pop();
1198       return this;
1199     }
1200 
1201     // If we are rendering explicit null values and the backend proto field is
1202     // not of the google.protobuf.NullType type, we do nothing.
1203     if (data.type() == DataPiece::TYPE_NULL &&
1204         field->type_url() != kStructNullValueTypeUrl) {
1205       Pop();
1206       return this;
1207     }
1208 
1209     // Render the map value as a primitive type.
1210     ProtoWriter::RenderDataPiece("value", data);
1211     Pop();
1212     return this;
1213   }
1214 
1215   field = Lookup(name);
1216   if (field == nullptr) return this;
1217 
1218   // Check if the field is of special type. Render it accordingly if so.
1219   const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url());
1220   if (type_renderer != nullptr) {
1221     // Pass through null value only for google.protobuf.Value. For other
1222     // types we ignore null value just like for regular field types.
1223     if (data.type() != DataPiece::TYPE_NULL ||
1224         field->type_url() == kStructValueTypeUrl) {
1225       Push(name, Item::MESSAGE, false, false);
1226       status = (*type_renderer)(this, data);
1227       if (!status.ok()) {
1228         InvalidValue(field->type_url(),
1229                      StrCat("Field '", name, "', ", status.message()));
1230       }
1231       Pop();
1232     }
1233     return this;
1234   }
1235 
1236   // If we are rendering explicit null values and the backend proto field is
1237   // not of the google.protobuf.NullType type, we do nothing.
1238   if (data.type() == DataPiece::TYPE_NULL &&
1239       field->type_url() != kStructNullValueTypeUrl) {
1240     return this;
1241   }
1242 
1243   ProtoWriter::RenderDataPiece(name, data);
1244   return this;
1245 }
1246 
1247 // Map of functions that are responsible for rendering well known type
1248 // represented by the key.
1249 std::unordered_map<std::string, ProtoStreamObjectWriter::TypeRenderer>*
1250     ProtoStreamObjectWriter::renderers_ = nullptr;
1251 PROTOBUF_NAMESPACE_ID::internal::once_flag writer_renderers_init_;
1252 
InitRendererMap()1253 void ProtoStreamObjectWriter::InitRendererMap() {
1254   renderers_ = new std::unordered_map<std::string,
1255                                       ProtoStreamObjectWriter::TypeRenderer>();
1256   (*renderers_)["type.googleapis.com/google.protobuf.Timestamp"] =
1257       &ProtoStreamObjectWriter::RenderTimestamp;
1258   (*renderers_)["type.googleapis.com/google.protobuf.Duration"] =
1259       &ProtoStreamObjectWriter::RenderDuration;
1260   (*renderers_)["type.googleapis.com/google.protobuf.FieldMask"] =
1261       &ProtoStreamObjectWriter::RenderFieldMask;
1262   (*renderers_)["type.googleapis.com/google.protobuf.Double"] =
1263       &ProtoStreamObjectWriter::RenderWrapperType;
1264   (*renderers_)["type.googleapis.com/google.protobuf.Float"] =
1265       &ProtoStreamObjectWriter::RenderWrapperType;
1266   (*renderers_)["type.googleapis.com/google.protobuf.Int64"] =
1267       &ProtoStreamObjectWriter::RenderWrapperType;
1268   (*renderers_)["type.googleapis.com/google.protobuf.UInt64"] =
1269       &ProtoStreamObjectWriter::RenderWrapperType;
1270   (*renderers_)["type.googleapis.com/google.protobuf.Int32"] =
1271       &ProtoStreamObjectWriter::RenderWrapperType;
1272   (*renderers_)["type.googleapis.com/google.protobuf.UInt32"] =
1273       &ProtoStreamObjectWriter::RenderWrapperType;
1274   (*renderers_)["type.googleapis.com/google.protobuf.Bool"] =
1275       &ProtoStreamObjectWriter::RenderWrapperType;
1276   (*renderers_)["type.googleapis.com/google.protobuf.String"] =
1277       &ProtoStreamObjectWriter::RenderWrapperType;
1278   (*renderers_)["type.googleapis.com/google.protobuf.Bytes"] =
1279       &ProtoStreamObjectWriter::RenderWrapperType;
1280   (*renderers_)["type.googleapis.com/google.protobuf.DoubleValue"] =
1281       &ProtoStreamObjectWriter::RenderWrapperType;
1282   (*renderers_)["type.googleapis.com/google.protobuf.FloatValue"] =
1283       &ProtoStreamObjectWriter::RenderWrapperType;
1284   (*renderers_)["type.googleapis.com/google.protobuf.Int64Value"] =
1285       &ProtoStreamObjectWriter::RenderWrapperType;
1286   (*renderers_)["type.googleapis.com/google.protobuf.UInt64Value"] =
1287       &ProtoStreamObjectWriter::RenderWrapperType;
1288   (*renderers_)["type.googleapis.com/google.protobuf.Int32Value"] =
1289       &ProtoStreamObjectWriter::RenderWrapperType;
1290   (*renderers_)["type.googleapis.com/google.protobuf.UInt32Value"] =
1291       &ProtoStreamObjectWriter::RenderWrapperType;
1292   (*renderers_)["type.googleapis.com/google.protobuf.BoolValue"] =
1293       &ProtoStreamObjectWriter::RenderWrapperType;
1294   (*renderers_)["type.googleapis.com/google.protobuf.StringValue"] =
1295       &ProtoStreamObjectWriter::RenderWrapperType;
1296   (*renderers_)["type.googleapis.com/google.protobuf.BytesValue"] =
1297       &ProtoStreamObjectWriter::RenderWrapperType;
1298   (*renderers_)["type.googleapis.com/google.protobuf.Value"] =
1299       &ProtoStreamObjectWriter::RenderStructValue;
1300   ::google::protobuf::internal::OnShutdown(&DeleteRendererMap);
1301 }
1302 
DeleteRendererMap()1303 void ProtoStreamObjectWriter::DeleteRendererMap() {
1304   delete ProtoStreamObjectWriter::renderers_;
1305   renderers_ = nullptr;
1306 }
1307 
1308 ProtoStreamObjectWriter::TypeRenderer*
FindTypeRenderer(const std::string & type_url)1309 ProtoStreamObjectWriter::FindTypeRenderer(const std::string& type_url) {
1310   PROTOBUF_NAMESPACE_ID::internal::call_once(writer_renderers_init_,
1311                                              InitRendererMap);
1312   return FindOrNull(*renderers_, type_url);
1313 }
1314 
ValidMapKey(StringPiece unnormalized_name)1315 bool ProtoStreamObjectWriter::ValidMapKey(StringPiece unnormalized_name) {
1316   if (current_ == nullptr) return true;
1317 
1318   if (!current_->InsertMapKeyIfNotPresent(unnormalized_name)) {
1319     listener()->InvalidName(
1320         location(), unnormalized_name,
1321         StrCat("Repeated map key: '", unnormalized_name,
1322                      "' is already set."));
1323     return false;
1324   }
1325 
1326   return true;
1327 }
1328 
Push(StringPiece name,Item::ItemType item_type,bool is_placeholder,bool is_list)1329 void ProtoStreamObjectWriter::Push(
1330     StringPiece name, Item::ItemType item_type, bool is_placeholder,
1331     bool is_list) {
1332   is_list ? ProtoWriter::StartList(name) : ProtoWriter::StartObject(name);
1333 
1334   // invalid_depth == 0 means it is a successful StartObject or StartList.
1335   if (invalid_depth() == 0)
1336     current_.reset(
1337         new Item(current_.release(), item_type, is_placeholder, is_list));
1338 }
1339 
Pop()1340 void ProtoStreamObjectWriter::Pop() {
1341   // Pop all placeholder items sending StartObject or StartList events to
1342   // ProtoWriter according to is_list value.
1343   while (current_ != nullptr && current_->is_placeholder()) {
1344     PopOneElement();
1345   }
1346   if (current_ != nullptr) {
1347     PopOneElement();
1348   }
1349 }
1350 
PopOneElement()1351 void ProtoStreamObjectWriter::PopOneElement() {
1352   current_->is_list() ? ProtoWriter::EndList() : ProtoWriter::EndObject();
1353   current_.reset(current_->pop<Item>());
1354 }
1355 
IsMap(const google::protobuf::Field & field)1356 bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) {
1357   if (field.type_url().empty() ||
1358       field.kind() != google::protobuf::Field::TYPE_MESSAGE ||
1359       field.cardinality() != google::protobuf::Field::CARDINALITY_REPEATED) {
1360     return false;
1361   }
1362   const google::protobuf::Type* field_type =
1363       typeinfo()->GetTypeByTypeUrl(field.type_url());
1364 
1365   return converter::IsMap(field, *field_type);
1366 }
1367 
IsAny(const google::protobuf::Field & field)1368 bool ProtoStreamObjectWriter::IsAny(const google::protobuf::Field& field) {
1369   return GetTypeWithoutUrl(field.type_url()) == kAnyType;
1370 }
1371 
IsStruct(const google::protobuf::Field & field)1372 bool ProtoStreamObjectWriter::IsStruct(const google::protobuf::Field& field) {
1373   return GetTypeWithoutUrl(field.type_url()) == kStructType;
1374 }
1375 
IsStructValue(const google::protobuf::Field & field)1376 bool ProtoStreamObjectWriter::IsStructValue(
1377     const google::protobuf::Field& field) {
1378   return GetTypeWithoutUrl(field.type_url()) == kStructValueType;
1379 }
1380 
IsStructListValue(const google::protobuf::Field & field)1381 bool ProtoStreamObjectWriter::IsStructListValue(
1382     const google::protobuf::Field& field) {
1383   return GetTypeWithoutUrl(field.type_url()) == kStructListValueType;
1384 }
1385 
1386 }  // namespace converter
1387 }  // namespace util
1388 }  // namespace protobuf
1389 }  // namespace google
1390