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