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