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