• 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/time.h>
39 #include <google/protobuf/stubs/once.h>
40 #include <google/protobuf/wire_format_lite.h>
41 #include <google/protobuf/util/internal/field_mask_utility.h>
42 #include <google/protobuf/util/internal/object_location_tracker.h>
43 #include <google/protobuf/util/internal/constants.h>
44 #include <google/protobuf/util/internal/utility.h>
45 #include <google/protobuf/stubs/strutil.h>
46 
47 
48 #include <google/protobuf/stubs/map_util.h>
49 #include <google/protobuf/stubs/statusor.h>
50 
51 
52 #include <google/protobuf/port_def.inc>
53 
54 namespace google {
55 namespace protobuf {
56 namespace util {
57 namespace converter {
58 
59 using ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite;
60 using std::placeholders::_1;
61 using util::Status;
62 using util::StatusOr;
63 using util::error::INVALID_ARGUMENT;
64 
65 
ProtoStreamObjectWriter(TypeResolver * type_resolver,const google::protobuf::Type & type,strings::ByteSink * output,ErrorListener * listener,const ProtoStreamObjectWriter::Options & options)66 ProtoStreamObjectWriter::ProtoStreamObjectWriter(
67     TypeResolver* type_resolver, const google::protobuf::Type& type,
68     strings::ByteSink* output, ErrorListener* listener,
69     const ProtoStreamObjectWriter::Options& options)
70     : ProtoWriter(type_resolver, type, output, listener),
71       master_type_(type),
72       current_(nullptr),
73       options_(options) {
74   set_ignore_unknown_fields(options_.ignore_unknown_fields);
75   set_ignore_unknown_enum_values(options_.ignore_unknown_enum_values);
76   set_use_lower_camel_for_enums(options_.use_lower_camel_for_enums);
77   set_case_insensitive_enum_parsing(options_.case_insensitive_enum_parsing);
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 }
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     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.ValueOrDie();
340   }
341   // Resolve the type url, and report an error if we failed to resolve it.
342   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.ValueOrDie();
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 
StartObject(StringPiece name)485 ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartObject(
486     StringPiece name) {
487   if (invalid_depth() > 0) {
488     IncrementInvalidDepth();
489     return this;
490   }
491 
492   // Starting the root message. Create the root Item and return.
493   // ANY message type does not need special handling, just set the ItemType
494   // to ANY.
495   if (current_ == nullptr) {
496     ProtoWriter::StartObject(name);
497     current_.reset(new Item(
498         this, master_type_.name() == kAnyType ? Item::ANY : Item::MESSAGE,
499         false, false));
500 
501     // If master type is a special type that needs extra values to be written to
502     // stream, we write those values.
503     if (master_type_.name() == kStructType) {
504       // Struct has a map<string, Value> field called "fields".
505       // https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/struct.proto
506       // "fields": [
507       Push("fields", Item::MAP, true, true);
508       return this;
509     }
510 
511     if (master_type_.name() == kStructValueType) {
512       // We got a StartObject call with google.protobuf.Value field. The only
513       // object within that type is a struct type. So start a struct.
514       //
515       // The struct field in Value type is named "struct_value"
516       // https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/struct.proto
517       // Also start the map field "fields" within the struct.
518       // "struct_value": {
519       //   "fields": [
520       Push("struct_value", Item::MESSAGE, true, false);
521       Push("fields", Item::MAP, true, true);
522       return this;
523     }
524 
525     if (master_type_.name() == kStructListValueType) {
526       InvalidValue(kStructListValueType,
527                    "Cannot start root message with ListValue.");
528     }
529 
530     return this;
531   }
532 
533   // Send all ANY events to AnyWriter.
534   if (current_->IsAny()) {
535     current_->any()->StartObject(name);
536     return this;
537   }
538 
539   // If we are within a map, we render name as keys and send StartObject to the
540   // value field.
541   if (current_->IsMap()) {
542     if (!ValidMapKey(name)) {
543       IncrementInvalidDepth();
544       return this;
545     }
546 
547     // Map is a repeated field of message type with a "key" and a "value" field.
548     // https://developers.google.com/protocol-buffers/docs/proto3?hl=en#maps
549     // message MapFieldEntry {
550     //   key_type key = 1;
551     //   value_type value = 2;
552     // }
553     //
554     // repeated MapFieldEntry map_field = N;
555     //
556     // That means, we render the following element within a list (hence no
557     // name):
558     // { "key": "<name>", "value": {
559     Push("", Item::MESSAGE, false, false);
560     ProtoWriter::RenderDataPiece("key",
561                                  DataPiece(name, use_strict_base64_decoding()));
562     Push("value", IsAny(*Lookup("value")) ? Item::ANY : Item::MESSAGE, true,
563          false);
564 
565     // Make sure we are valid so far after starting map fields.
566     if (invalid_depth() > 0) return this;
567 
568     // If top of stack is g.p.Struct type, start the struct the map field within
569     // it.
570     if (element() != nullptr && IsStruct(*element()->parent_field())) {
571       // Render "fields": [
572       Push("fields", Item::MAP, true, true);
573       return this;
574     }
575 
576     // If top of stack is g.p.Value type, start the Struct within it.
577     if (element() != nullptr && IsStructValue(*element()->parent_field())) {
578       // Render
579       // "struct_value": {
580       //   "fields": [
581       Push("struct_value", Item::MESSAGE, true, false);
582       Push("fields", Item::MAP, true, true);
583     }
584     return this;
585   }
586 
587   const google::protobuf::Field* field = BeginNamed(name, false);
588   if (field == nullptr) return this;
589 
590   if (IsStruct(*field)) {
591     // Start a struct object.
592     // Render
593     // "<name>": {
594     //   "fields": {
595     Push(name, Item::MESSAGE, false, false);
596     Push("fields", Item::MAP, true, true);
597     return this;
598   }
599 
600   if (IsStructValue(*field)) {
601     // We got a StartObject call with google.protobuf.Value field.  The only
602     // object within that type is a struct type. So start a struct.
603     // Render
604     // "<name>": {
605     //   "struct_value": {
606     //     "fields": {
607     Push(name, Item::MESSAGE, false, false);
608     Push("struct_value", Item::MESSAGE, true, false);
609     Push("fields", Item::MAP, true, true);
610     return this;
611   }
612 
613   if (IsMap(*field)) {
614     // Begin a map. A map is triggered by a StartObject() call if the current
615     // field has a map type.
616     // A map type is always repeated, hence set is_list to true.
617     // Render
618     // "<name>": [
619     Push(name, Item::MAP, false, true);
620     return this;
621   }
622 
623   // A regular message type. Pass it directly to ProtoWriter.
624   // Render
625   // "<name>": {
626   Push(name, IsAny(*field) ? Item::ANY : Item::MESSAGE, false, false);
627   return this;
628 }
629 
EndObject()630 ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndObject() {
631   if (invalid_depth() > 0) {
632     DecrementInvalidDepth();
633     return this;
634   }
635 
636   if (current_ == nullptr) return this;
637 
638   if (current_->IsAny()) {
639     if (current_->any()->EndObject()) return this;
640   }
641 
642   Pop();
643 
644   return this;
645 }
646 
StartList(StringPiece name)647 ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartList(
648     StringPiece name) {
649   if (invalid_depth() > 0) {
650     IncrementInvalidDepth();
651     return this;
652   }
653 
654   // Since we cannot have a top-level repeated item in protobuf, the only way
655   // this is valid is if we start a special type google.protobuf.ListValue or
656   // google.protobuf.Value.
657   if (current_ == nullptr) {
658     if (!name.empty()) {
659       InvalidName(name, "Root element should not be named.");
660       IncrementInvalidDepth();
661       return this;
662     }
663 
664     // If master type is a special type that needs extra values to be written to
665     // stream, we write those values.
666     if (master_type_.name() == kStructValueType) {
667       // We got a StartList with google.protobuf.Value master type. This means
668       // we have to start the "list_value" within google.protobuf.Value.
669       //
670       // See
671       // https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/struct.proto
672       //
673       // Render
674       // "<name>": {
675       //   "list_value": {
676       //     "values": [  // Start this list.
677       ProtoWriter::StartObject(name);
678       current_.reset(new Item(this, Item::MESSAGE, false, false));
679       Push("list_value", Item::MESSAGE, true, false);
680       Push("values", Item::MESSAGE, true, true);
681       return this;
682     }
683 
684     if (master_type_.name() == kStructListValueType) {
685       // We got a StartList with google.protobuf.ListValue master type. This
686       // means we have to start the "values" within google.protobuf.ListValue.
687       //
688       // Render
689       // "<name>": {
690       //   "values": [  // Start this list.
691       ProtoWriter::StartObject(name);
692       current_.reset(new Item(this, Item::MESSAGE, false, false));
693       Push("values", Item::MESSAGE, true, true);
694       return this;
695     }
696 
697     // Send the event to ProtoWriter so proper errors can be reported.
698     //
699     // Render a regular list:
700     // "<name>": [
701     ProtoWriter::StartList(name);
702     current_.reset(new Item(this, Item::MESSAGE, false, true));
703     return this;
704   }
705 
706   if (current_->IsAny()) {
707     current_->any()->StartList(name);
708     return this;
709   }
710 
711   // If the top of stack is a map, we are starting a list value within a map.
712   // Since map does not allow repeated values, this can only happen when the map
713   // value is of a special type that renders a list in JSON.  These can be one
714   // of 3 cases:
715   // i. We are rendering a list value within google.protobuf.Struct
716   // ii. We are rendering a list value within google.protobuf.Value
717   // iii. We are rendering a list value with type google.protobuf.ListValue.
718   if (current_->IsMap()) {
719     if (!ValidMapKey(name)) {
720       IncrementInvalidDepth();
721       return this;
722     }
723 
724     // Start the repeated map entry object.
725     // Render
726     // { "key": "<name>", "value": {
727     Push("", Item::MESSAGE, false, false);
728     ProtoWriter::RenderDataPiece("key",
729                                  DataPiece(name, use_strict_base64_decoding()));
730     Push("value", Item::MESSAGE, true, false);
731 
732     // Make sure we are valid after pushing all above items.
733     if (invalid_depth() > 0) return this;
734 
735     // case i and ii above. Start "list_value" field within g.p.Value
736     if (element() != nullptr && element()->parent_field() != nullptr) {
737       // Render
738       // "list_value": {
739       //   "values": [  // Start this list
740       if (IsStructValue(*element()->parent_field())) {
741         Push("list_value", Item::MESSAGE, true, false);
742         Push("values", Item::MESSAGE, true, true);
743         return this;
744       }
745 
746       // Render
747       // "values": [
748       if (IsStructListValue(*element()->parent_field())) {
749         // case iii above. Bind directly to g.p.ListValue
750         Push("values", Item::MESSAGE, true, true);
751         return this;
752       }
753     }
754 
755     // Report an error.
756     InvalidValue("Map", StrCat("Cannot have repeated items ('", name,
757                                      "') within a map."));
758     return this;
759   }
760 
761   // When name is empty and stack is not empty, we are rendering an item within
762   // a list.
763   if (name.empty()) {
764     if (element() != nullptr && element()->parent_field() != nullptr) {
765       if (IsStructValue(*element()->parent_field())) {
766         // Since it is g.p.Value, we bind directly to the list_value.
767         // Render
768         // {  // g.p.Value item within the list
769         //   "list_value": {
770         //     "values": [
771         Push("", Item::MESSAGE, false, false);
772         Push("list_value", Item::MESSAGE, true, false);
773         Push("values", Item::MESSAGE, true, true);
774         return this;
775       }
776 
777       if (IsStructListValue(*element()->parent_field())) {
778         // Since it is g.p.ListValue, we bind to it directly.
779         // Render
780         // {  // g.p.ListValue item within the list
781         //   "values": [
782         Push("", Item::MESSAGE, false, false);
783         Push("values", Item::MESSAGE, true, true);
784         return this;
785       }
786     }
787 
788     // Pass the event to underlying ProtoWriter.
789     Push(name, Item::MESSAGE, false, true);
790     return this;
791   }
792 
793   // name is not empty
794   const google::protobuf::Field* field = Lookup(name);
795   if (field == nullptr) {
796     IncrementInvalidDepth();
797     return this;
798   }
799 
800   if (IsStructValue(*field)) {
801     // If g.p.Value is repeated, start that list. Otherwise, start the
802     // "list_value" within it.
803     if (IsRepeated(*field)) {
804       // Render it just like a regular repeated field.
805       // "<name>": [
806       Push(name, Item::MESSAGE, false, true);
807       return this;
808     }
809 
810     // Start the "list_value" field.
811     // Render
812     // "<name>": {
813     //   "list_value": {
814     //     "values": [
815     Push(name, Item::MESSAGE, false, false);
816     Push("list_value", Item::MESSAGE, true, false);
817     Push("values", Item::MESSAGE, true, true);
818     return this;
819   }
820 
821   if (IsStructListValue(*field)) {
822     // If g.p.ListValue is repeated, start that list. Otherwise, start the
823     // "values" within it.
824     if (IsRepeated(*field)) {
825       // Render it just like a regular repeated field.
826       // "<name>": [
827       Push(name, Item::MESSAGE, false, true);
828       return this;
829     }
830 
831     // Start the "values" field within g.p.ListValue.
832     // Render
833     // "<name>": {
834     //   "values": [
835     Push(name, Item::MESSAGE, false, false);
836     Push("values", Item::MESSAGE, true, true);
837     return this;
838   }
839 
840   // If we are here, the field should be repeated. Report an error otherwise.
841   if (!IsRepeated(*field)) {
842     IncrementInvalidDepth();
843     InvalidName(name, "Proto field is not repeating, cannot start list.");
844     return this;
845   }
846 
847   if (IsMap(*field)) {
848     InvalidValue("Map", StrCat("Cannot bind a list to map for field '",
849                                      name, "'."));
850     IncrementInvalidDepth();
851     return this;
852   }
853 
854   // Pass the event to ProtoWriter.
855   // Render
856   // "<name>": [
857   Push(name, Item::MESSAGE, false, true);
858   return this;
859 }
860 
EndList()861 ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndList() {
862   if (invalid_depth() > 0) {
863     DecrementInvalidDepth();
864     return this;
865   }
866 
867   if (current_ == nullptr) return this;
868 
869   if (current_->IsAny()) {
870     current_->any()->EndList();
871     return this;
872   }
873 
874   Pop();
875   return this;
876 }
877 
RenderStructValue(ProtoStreamObjectWriter * ow,const DataPiece & data)878 Status ProtoStreamObjectWriter::RenderStructValue(ProtoStreamObjectWriter* ow,
879                                                   const DataPiece& data) {
880   std::string struct_field_name;
881   switch (data.type()) {
882     // Our JSON parser parses numbers as either int64, uint64, or double.
883     case DataPiece::TYPE_INT64: {
884       // If the option to treat integers as strings is set, then render them as
885       // strings. Otherwise, fallback to rendering them as double.
886       if (ow->options_.struct_integers_as_strings) {
887         StatusOr<int64> int_value = data.ToInt64();
888         if (int_value.ok()) {
889           ow->ProtoWriter::RenderDataPiece(
890               "string_value",
891               DataPiece(StrCat(int_value.ValueOrDie()), true));
892           return Status();
893         }
894       }
895       struct_field_name = "number_value";
896       break;
897     }
898     case DataPiece::TYPE_UINT64: {
899       // If the option to treat integers as strings is set, then render them as
900       // strings. Otherwise, fallback to rendering them as double.
901       if (ow->options_.struct_integers_as_strings) {
902         StatusOr<uint64> int_value = data.ToUint64();
903         if (int_value.ok()) {
904           ow->ProtoWriter::RenderDataPiece(
905               "string_value",
906               DataPiece(StrCat(int_value.ValueOrDie()), true));
907           return Status();
908         }
909       }
910       struct_field_name = "number_value";
911       break;
912     }
913     case DataPiece::TYPE_DOUBLE: {
914       if (ow->options_.struct_integers_as_strings) {
915         StatusOr<double> double_value = data.ToDouble();
916         if (double_value.ok()) {
917           ow->ProtoWriter::RenderDataPiece(
918               "string_value",
919               DataPiece(SimpleDtoa(double_value.ValueOrDie()), true));
920           return Status();
921         }
922       }
923       struct_field_name = "number_value";
924       break;
925     }
926     case DataPiece::TYPE_STRING: {
927       struct_field_name = "string_value";
928       break;
929     }
930     case DataPiece::TYPE_BOOL: {
931       struct_field_name = "bool_value";
932       break;
933     }
934     case DataPiece::TYPE_NULL: {
935       struct_field_name = "null_value";
936       break;
937     }
938     default: {
939       return Status(util::error::INVALID_ARGUMENT,
940                     "Invalid struct data type. Only number, string, boolean or "
941                     "null values are supported.");
942     }
943   }
944   ow->ProtoWriter::RenderDataPiece(struct_field_name, data);
945   return Status();
946 }
947 
RenderTimestamp(ProtoStreamObjectWriter * ow,const DataPiece & data)948 Status ProtoStreamObjectWriter::RenderTimestamp(ProtoStreamObjectWriter* ow,
949                                                 const DataPiece& data) {
950   if (data.type() == DataPiece::TYPE_NULL) return Status();
951   if (data.type() != DataPiece::TYPE_STRING) {
952     return Status(util::error::INVALID_ARGUMENT,
953                   StrCat("Invalid data type for timestamp, value is ",
954                                data.ValueAsStringOrDefault("")));
955   }
956 
957   StringPiece value(data.str());
958 
959   int64 seconds;
960   int32 nanos;
961   if (!::google::protobuf::internal::ParseTime(value.ToString(), &seconds,
962                                                &nanos)) {
963     return Status(INVALID_ARGUMENT, StrCat("Invalid time format: ", value));
964   }
965 
966 
967   ow->ProtoWriter::RenderDataPiece("seconds", DataPiece(seconds));
968   ow->ProtoWriter::RenderDataPiece("nanos", DataPiece(nanos));
969   return Status();
970 }
971 
RenderOneFieldPath(ProtoStreamObjectWriter * ow,StringPiece path)972 static inline util::Status RenderOneFieldPath(ProtoStreamObjectWriter* ow,
973                                                 StringPiece path) {
974   ow->ProtoWriter::RenderDataPiece(
975       "paths", DataPiece(ConvertFieldMaskPath(path, &ToSnakeCase), true));
976   return Status();
977 }
978 
RenderFieldMask(ProtoStreamObjectWriter * ow,const DataPiece & data)979 Status ProtoStreamObjectWriter::RenderFieldMask(ProtoStreamObjectWriter* ow,
980                                                 const DataPiece& data) {
981   if (data.type() == DataPiece::TYPE_NULL) return Status();
982   if (data.type() != DataPiece::TYPE_STRING) {
983     return Status(util::error::INVALID_ARGUMENT,
984                   StrCat("Invalid data type for field mask, value is ",
985                                data.ValueAsStringOrDefault("")));
986   }
987 
988   // TODO(tsun): figure out how to do proto descriptor based snake case
989   // conversions as much as possible. Because ToSnakeCase sometimes returns the
990   // wrong value.
991   return DecodeCompactFieldMaskPaths(data.str(),
992                                      std::bind(&RenderOneFieldPath, ow, _1));
993 }
994 
RenderDuration(ProtoStreamObjectWriter * ow,const DataPiece & data)995 Status ProtoStreamObjectWriter::RenderDuration(ProtoStreamObjectWriter* ow,
996                                                const DataPiece& data) {
997   if (data.type() == DataPiece::TYPE_NULL) return Status();
998   if (data.type() != DataPiece::TYPE_STRING) {
999     return Status(util::error::INVALID_ARGUMENT,
1000                   StrCat("Invalid data type for duration, value is ",
1001                                data.ValueAsStringOrDefault("")));
1002   }
1003 
1004   StringPiece value(data.str());
1005 
1006   if (!HasSuffixString(value, "s")) {
1007     return Status(util::error::INVALID_ARGUMENT,
1008                   "Illegal duration format; duration must end with 's'");
1009   }
1010   value = value.substr(0, value.size() - 1);
1011   int sign = 1;
1012   if (HasPrefixString(value, "-")) {
1013     sign = -1;
1014     value = value.substr(1);
1015   }
1016 
1017   StringPiece s_secs, s_nanos;
1018   SplitSecondsAndNanos(value, &s_secs, &s_nanos);
1019   uint64 unsigned_seconds;
1020   if (!safe_strtou64(s_secs, &unsigned_seconds)) {
1021     return Status(util::error::INVALID_ARGUMENT,
1022                   "Invalid duration format, failed to parse seconds");
1023   }
1024 
1025   int32 nanos = 0;
1026   Status nanos_status = GetNanosFromStringPiece(
1027       s_nanos, "Invalid duration format, failed to parse nano seconds",
1028       "Duration value exceeds limits", &nanos);
1029   if (!nanos_status.ok()) {
1030     return nanos_status;
1031   }
1032   nanos = sign * nanos;
1033 
1034   int64 seconds = sign * unsigned_seconds;
1035   if (seconds > kDurationMaxSeconds || seconds < kDurationMinSeconds ||
1036       nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) {
1037     return Status(util::error::INVALID_ARGUMENT,
1038                   "Duration value exceeds limits");
1039   }
1040 
1041   ow->ProtoWriter::RenderDataPiece("seconds", DataPiece(seconds));
1042   ow->ProtoWriter::RenderDataPiece("nanos", DataPiece(nanos));
1043   return Status();
1044 }
1045 
RenderWrapperType(ProtoStreamObjectWriter * ow,const DataPiece & data)1046 Status ProtoStreamObjectWriter::RenderWrapperType(ProtoStreamObjectWriter* ow,
1047                                                   const DataPiece& data) {
1048   if (data.type() == DataPiece::TYPE_NULL) return Status();
1049   ow->ProtoWriter::RenderDataPiece("value", data);
1050   return Status();
1051 }
1052 
RenderDataPiece(StringPiece name,const DataPiece & data)1053 ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece(
1054     StringPiece name, const DataPiece& data) {
1055   Status status;
1056   if (invalid_depth() > 0) return this;
1057 
1058   if (current_ == nullptr) {
1059     const TypeRenderer* type_renderer =
1060         FindTypeRenderer(GetFullTypeWithUrl(master_type_.name()));
1061     if (type_renderer == nullptr) {
1062       InvalidName(name, "Root element must be a message.");
1063       return this;
1064     }
1065     // Render the special type.
1066     // "<name>": {
1067     //   ... Render special type ...
1068     // }
1069     ProtoWriter::StartObject(name);
1070     status = (*type_renderer)(this, data);
1071     if (!status.ok()) {
1072       InvalidValue(master_type_.name(),
1073                    StrCat("Field '", name, "', ", status.message()));
1074     }
1075     ProtoWriter::EndObject();
1076     return this;
1077   }
1078 
1079   if (current_->IsAny()) {
1080     current_->any()->RenderDataPiece(name, data);
1081     return this;
1082   }
1083 
1084   const google::protobuf::Field* field = nullptr;
1085   if (current_->IsMap()) {
1086     if (!ValidMapKey(name)) return this;
1087 
1088     field = Lookup("value");
1089     if (field == nullptr) {
1090       GOOGLE_LOG(DFATAL) << "Map does not have a value field.";
1091       return this;
1092     }
1093 
1094     if (options_.ignore_null_value_map_entry) {
1095       // If we are rendering explicit null values and the backend proto field is
1096       // not of the google.protobuf.NullType type, interpret null as absence.
1097       if (data.type() == DataPiece::TYPE_NULL &&
1098           field->type_url() != kStructNullValueTypeUrl) {
1099         return this;
1100       }
1101     }
1102 
1103     // Render an item in repeated map list.
1104     // { "key": "<name>", "value":
1105     Push("", Item::MESSAGE, false, false);
1106     ProtoWriter::RenderDataPiece("key",
1107                                  DataPiece(name, use_strict_base64_decoding()));
1108 
1109     const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url());
1110     if (type_renderer != nullptr) {
1111       // Map's value type is a special type. Render it like a message:
1112       // "value": {
1113       //   ... Render special type ...
1114       // }
1115       Push("value", Item::MESSAGE, true, false);
1116       status = (*type_renderer)(this, data);
1117       if (!status.ok()) {
1118         InvalidValue(field->type_url(),
1119                      StrCat("Field '", name, "', ", status.message()));
1120       }
1121       Pop();
1122       return this;
1123     }
1124 
1125     // If we are rendering explicit null values and the backend proto field is
1126     // not of the google.protobuf.NullType type, we do nothing.
1127     if (data.type() == DataPiece::TYPE_NULL &&
1128         field->type_url() != kStructNullValueTypeUrl) {
1129       Pop();
1130       return this;
1131     }
1132 
1133     // Render the map value as a primitive type.
1134     ProtoWriter::RenderDataPiece("value", data);
1135     Pop();
1136     return this;
1137   }
1138 
1139   field = Lookup(name);
1140   if (field == nullptr) return this;
1141 
1142   // Check if the field is of special type. Render it accordingly if so.
1143   const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url());
1144   if (type_renderer != nullptr) {
1145     // Pass through null value only for google.protobuf.Value. For other
1146     // types we ignore null value just like for regular field types.
1147     if (data.type() != DataPiece::TYPE_NULL ||
1148         field->type_url() == kStructValueTypeUrl) {
1149       Push(name, Item::MESSAGE, false, false);
1150       status = (*type_renderer)(this, data);
1151       if (!status.ok()) {
1152         InvalidValue(field->type_url(),
1153                      StrCat("Field '", name, "', ", status.message()));
1154       }
1155       Pop();
1156     }
1157     return this;
1158   }
1159 
1160   // If we are rendering explicit null values and the backend proto field is
1161   // not of the google.protobuf.NullType type, we do nothing.
1162   if (data.type() == DataPiece::TYPE_NULL &&
1163       field->type_url() != kStructNullValueTypeUrl) {
1164     return this;
1165   }
1166 
1167   ProtoWriter::RenderDataPiece(name, data);
1168   return this;
1169 }
1170 
1171 // Map of functions that are responsible for rendering well known type
1172 // represented by the key.
1173 std::unordered_map<std::string, ProtoStreamObjectWriter::TypeRenderer>*
1174     ProtoStreamObjectWriter::renderers_ = NULL;
1175 PROTOBUF_NAMESPACE_ID::internal::once_flag writer_renderers_init_;
1176 
InitRendererMap()1177 void ProtoStreamObjectWriter::InitRendererMap() {
1178   renderers_ = new std::unordered_map<std::string,
1179                                       ProtoStreamObjectWriter::TypeRenderer>();
1180   (*renderers_)["type.googleapis.com/google.protobuf.Timestamp"] =
1181       &ProtoStreamObjectWriter::RenderTimestamp;
1182   (*renderers_)["type.googleapis.com/google.protobuf.Duration"] =
1183       &ProtoStreamObjectWriter::RenderDuration;
1184   (*renderers_)["type.googleapis.com/google.protobuf.FieldMask"] =
1185       &ProtoStreamObjectWriter::RenderFieldMask;
1186   (*renderers_)["type.googleapis.com/google.protobuf.Double"] =
1187       &ProtoStreamObjectWriter::RenderWrapperType;
1188   (*renderers_)["type.googleapis.com/google.protobuf.Float"] =
1189       &ProtoStreamObjectWriter::RenderWrapperType;
1190   (*renderers_)["type.googleapis.com/google.protobuf.Int64"] =
1191       &ProtoStreamObjectWriter::RenderWrapperType;
1192   (*renderers_)["type.googleapis.com/google.protobuf.UInt64"] =
1193       &ProtoStreamObjectWriter::RenderWrapperType;
1194   (*renderers_)["type.googleapis.com/google.protobuf.Int32"] =
1195       &ProtoStreamObjectWriter::RenderWrapperType;
1196   (*renderers_)["type.googleapis.com/google.protobuf.UInt32"] =
1197       &ProtoStreamObjectWriter::RenderWrapperType;
1198   (*renderers_)["type.googleapis.com/google.protobuf.Bool"] =
1199       &ProtoStreamObjectWriter::RenderWrapperType;
1200   (*renderers_)["type.googleapis.com/google.protobuf.String"] =
1201       &ProtoStreamObjectWriter::RenderWrapperType;
1202   (*renderers_)["type.googleapis.com/google.protobuf.Bytes"] =
1203       &ProtoStreamObjectWriter::RenderWrapperType;
1204   (*renderers_)["type.googleapis.com/google.protobuf.DoubleValue"] =
1205       &ProtoStreamObjectWriter::RenderWrapperType;
1206   (*renderers_)["type.googleapis.com/google.protobuf.FloatValue"] =
1207       &ProtoStreamObjectWriter::RenderWrapperType;
1208   (*renderers_)["type.googleapis.com/google.protobuf.Int64Value"] =
1209       &ProtoStreamObjectWriter::RenderWrapperType;
1210   (*renderers_)["type.googleapis.com/google.protobuf.UInt64Value"] =
1211       &ProtoStreamObjectWriter::RenderWrapperType;
1212   (*renderers_)["type.googleapis.com/google.protobuf.Int32Value"] =
1213       &ProtoStreamObjectWriter::RenderWrapperType;
1214   (*renderers_)["type.googleapis.com/google.protobuf.UInt32Value"] =
1215       &ProtoStreamObjectWriter::RenderWrapperType;
1216   (*renderers_)["type.googleapis.com/google.protobuf.BoolValue"] =
1217       &ProtoStreamObjectWriter::RenderWrapperType;
1218   (*renderers_)["type.googleapis.com/google.protobuf.StringValue"] =
1219       &ProtoStreamObjectWriter::RenderWrapperType;
1220   (*renderers_)["type.googleapis.com/google.protobuf.BytesValue"] =
1221       &ProtoStreamObjectWriter::RenderWrapperType;
1222   (*renderers_)["type.googleapis.com/google.protobuf.Value"] =
1223       &ProtoStreamObjectWriter::RenderStructValue;
1224   ::google::protobuf::internal::OnShutdown(&DeleteRendererMap);
1225 }
1226 
DeleteRendererMap()1227 void ProtoStreamObjectWriter::DeleteRendererMap() {
1228   delete ProtoStreamObjectWriter::renderers_;
1229   renderers_ = NULL;
1230 }
1231 
1232 ProtoStreamObjectWriter::TypeRenderer*
FindTypeRenderer(const std::string & type_url)1233 ProtoStreamObjectWriter::FindTypeRenderer(const std::string& type_url) {
1234   PROTOBUF_NAMESPACE_ID::internal::call_once(writer_renderers_init_,
1235                                              InitRendererMap);
1236   return FindOrNull(*renderers_, type_url);
1237 }
1238 
ValidMapKey(StringPiece unnormalized_name)1239 bool ProtoStreamObjectWriter::ValidMapKey(StringPiece unnormalized_name) {
1240   if (current_ == nullptr) return true;
1241 
1242   if (!current_->InsertMapKeyIfNotPresent(unnormalized_name)) {
1243     listener()->InvalidName(
1244         location(), unnormalized_name,
1245         StrCat("Repeated map key: '", unnormalized_name,
1246                      "' is already set."));
1247     return false;
1248   }
1249 
1250   return true;
1251 }
1252 
Push(StringPiece name,Item::ItemType item_type,bool is_placeholder,bool is_list)1253 void ProtoStreamObjectWriter::Push(StringPiece name,
1254                                    Item::ItemType item_type,
1255                                    bool is_placeholder, bool is_list) {
1256   is_list ? ProtoWriter::StartList(name) : ProtoWriter::StartObject(name);
1257 
1258   // invalid_depth == 0 means it is a successful StartObject or StartList.
1259   if (invalid_depth() == 0)
1260     current_.reset(
1261         new Item(current_.release(), item_type, is_placeholder, is_list));
1262 }
1263 
Pop()1264 void ProtoStreamObjectWriter::Pop() {
1265   // Pop all placeholder items sending StartObject or StartList events to
1266   // ProtoWriter according to is_list value.
1267   while (current_ != nullptr && current_->is_placeholder()) {
1268     PopOneElement();
1269   }
1270   if (current_ != nullptr) {
1271     PopOneElement();
1272   }
1273 }
1274 
PopOneElement()1275 void ProtoStreamObjectWriter::PopOneElement() {
1276   current_->is_list() ? ProtoWriter::EndList() : ProtoWriter::EndObject();
1277   current_.reset(current_->pop<Item>());
1278 }
1279 
IsMap(const google::protobuf::Field & field)1280 bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) {
1281   if (field.type_url().empty() ||
1282       field.kind() != google::protobuf::Field_Kind_TYPE_MESSAGE ||
1283       field.cardinality() !=
1284           google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
1285     return false;
1286   }
1287   const google::protobuf::Type* field_type =
1288       typeinfo()->GetTypeByTypeUrl(field.type_url());
1289 
1290   return converter::IsMap(field, *field_type);
1291 }
1292 
IsAny(const google::protobuf::Field & field)1293 bool ProtoStreamObjectWriter::IsAny(const google::protobuf::Field& field) {
1294   return GetTypeWithoutUrl(field.type_url()) == kAnyType;
1295 }
1296 
IsStruct(const google::protobuf::Field & field)1297 bool ProtoStreamObjectWriter::IsStruct(const google::protobuf::Field& field) {
1298   return GetTypeWithoutUrl(field.type_url()) == kStructType;
1299 }
1300 
IsStructValue(const google::protobuf::Field & field)1301 bool ProtoStreamObjectWriter::IsStructValue(
1302     const google::protobuf::Field& field) {
1303   return GetTypeWithoutUrl(field.type_url()) == kStructValueType;
1304 }
1305 
IsStructListValue(const google::protobuf::Field & field)1306 bool ProtoStreamObjectWriter::IsStructListValue(
1307     const google::protobuf::Field& field) {
1308   return GetTypeWithoutUrl(field.type_url()) == kStructListValueType;
1309 }
1310 
1311 }  // namespace converter
1312 }  // namespace util
1313 }  // namespace protobuf
1314 }  // namespace google
1315