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