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