xref: /aosp_15_r20/external/cronet/third_party/protobuf/src/google/protobuf/util/internal/proto_writer.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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/proto_writer.h>
32 
33 #include <cstdint>
34 #include <functional>
35 #include <stack>
36 #include <unordered_set>
37 
38 #include <google/protobuf/stubs/once.h>
39 #include <google/protobuf/wire_format_lite.h>
40 #include <google/protobuf/stubs/strutil.h>
41 #include <google/protobuf/stubs/statusor.h>
42 #include <google/protobuf/stubs/time.h>
43 #include <google/protobuf/util/internal/constants.h>
44 #include <google/protobuf/util/internal/field_mask_utility.h>
45 #include <google/protobuf/util/internal/object_location_tracker.h>
46 #include <google/protobuf/util/internal/utility.h>
47 #include <google/protobuf/stubs/map_util.h>
48 
49 
50 // Must be included last.
51 #include <google/protobuf/port_def.inc>
52 
53 namespace google {
54 namespace protobuf {
55 namespace util {
56 namespace converter {
57 
58 using io::CodedOutputStream;
59 using ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite;
60 
ProtoWriter(TypeResolver * type_resolver,const google::protobuf::Type & type,strings::ByteSink * output,ErrorListener * listener)61 ProtoWriter::ProtoWriter(TypeResolver* type_resolver,
62                          const google::protobuf::Type& type,
63                          strings::ByteSink* output, ErrorListener* listener)
64     : master_type_(type),
65       typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
66       own_typeinfo_(true),
67       done_(false),
68       ignore_unknown_fields_(false),
69       ignore_unknown_enum_values_(false),
70       use_lower_camel_for_enums_(false),
71       case_insensitive_enum_parsing_(true),
72       use_json_name_in_missing_fields_(false),
73       element_(nullptr),
74       size_insert_(),
75       output_(output),
76       buffer_(),
77       adapter_(&buffer_),
78       stream_(new CodedOutputStream(&adapter_)),
79       listener_(listener),
80       invalid_depth_(0),
81       tracker_(new ObjectLocationTracker()) {}
82 
ProtoWriter(const TypeInfo * typeinfo,const google::protobuf::Type & type,strings::ByteSink * output,ErrorListener * listener)83 ProtoWriter::ProtoWriter(const TypeInfo* typeinfo,
84                          const google::protobuf::Type& type,
85                          strings::ByteSink* output, ErrorListener* listener)
86     : master_type_(type),
87       typeinfo_(typeinfo),
88       own_typeinfo_(false),
89       done_(false),
90       ignore_unknown_fields_(false),
91       ignore_unknown_enum_values_(false),
92       use_lower_camel_for_enums_(false),
93       case_insensitive_enum_parsing_(true),
94       use_json_name_in_missing_fields_(false),
95       element_(nullptr),
96       size_insert_(),
97       output_(output),
98       buffer_(),
99       adapter_(&buffer_),
100       stream_(new CodedOutputStream(&adapter_)),
101       listener_(listener),
102       invalid_depth_(0),
103       tracker_(new ObjectLocationTracker()) {}
104 
~ProtoWriter()105 ProtoWriter::~ProtoWriter() {
106   if (own_typeinfo_) {
107     delete typeinfo_;
108   }
109   if (element_ == nullptr) return;
110   // Cleanup explicitly in order to avoid destructor stack overflow when input
111   // is deeply nested.
112   // Cast to BaseElement to avoid doing additional checks (like missing fields)
113   // during pop().
114   std::unique_ptr<BaseElement> element(
115       static_cast<BaseElement*>(element_.get())->pop<BaseElement>());
116   while (element != nullptr) {
117     element.reset(element->pop<BaseElement>());
118   }
119 }
120 
121 namespace {
122 
123 // Writes an INT32 field, including tag to the stream.
WriteInt32(int field_number,const DataPiece & data,CodedOutputStream * stream)124 inline util::Status WriteInt32(int field_number, const DataPiece& data,
125                                CodedOutputStream* stream) {
126   util::StatusOr<int32_t> i32 = data.ToInt32();
127   if (i32.ok()) {
128     WireFormatLite::WriteInt32(field_number, i32.value(), stream);
129   }
130   return i32.status();
131 }
132 
133 // writes an SFIXED32 field, including tag, to the stream.
WriteSFixed32(int field_number,const DataPiece & data,CodedOutputStream * stream)134 inline util::Status WriteSFixed32(int field_number, const DataPiece& data,
135                                   CodedOutputStream* stream) {
136   util::StatusOr<int32_t> i32 = data.ToInt32();
137   if (i32.ok()) {
138     WireFormatLite::WriteSFixed32(field_number, i32.value(), stream);
139   }
140   return i32.status();
141 }
142 
143 // Writes an SINT32 field, including tag, to the stream.
WriteSInt32(int field_number,const DataPiece & data,CodedOutputStream * stream)144 inline util::Status WriteSInt32(int field_number, const DataPiece& data,
145                                 CodedOutputStream* stream) {
146   util::StatusOr<int32_t> i32 = data.ToInt32();
147   if (i32.ok()) {
148     WireFormatLite::WriteSInt32(field_number, i32.value(), stream);
149   }
150   return i32.status();
151 }
152 
153 // Writes a FIXED32 field, including tag, to the stream.
WriteFixed32(int field_number,const DataPiece & data,CodedOutputStream * stream)154 inline util::Status WriteFixed32(int field_number, const DataPiece& data,
155                                  CodedOutputStream* stream) {
156   util::StatusOr<uint32_t> u32 = data.ToUint32();
157   if (u32.ok()) {
158     WireFormatLite::WriteFixed32(field_number, u32.value(), stream);
159   }
160   return u32.status();
161 }
162 
163 // Writes a UINT32 field, including tag, to the stream.
WriteUInt32(int field_number,const DataPiece & data,CodedOutputStream * stream)164 inline util::Status WriteUInt32(int field_number, const DataPiece& data,
165                                 CodedOutputStream* stream) {
166   util::StatusOr<uint32_t> u32 = data.ToUint32();
167   if (u32.ok()) {
168     WireFormatLite::WriteUInt32(field_number, u32.value(), stream);
169   }
170   return u32.status();
171 }
172 
173 // Writes an INT64 field, including tag, to the stream.
WriteInt64(int field_number,const DataPiece & data,CodedOutputStream * stream)174 inline util::Status WriteInt64(int field_number, const DataPiece& data,
175                                CodedOutputStream* stream) {
176   util::StatusOr<int64_t> i64 = data.ToInt64();
177   if (i64.ok()) {
178     WireFormatLite::WriteInt64(field_number, i64.value(), stream);
179   }
180   return i64.status();
181 }
182 
183 // Writes an SFIXED64 field, including tag, to the stream.
WriteSFixed64(int field_number,const DataPiece & data,CodedOutputStream * stream)184 inline util::Status WriteSFixed64(int field_number, const DataPiece& data,
185                                   CodedOutputStream* stream) {
186   util::StatusOr<int64_t> i64 = data.ToInt64();
187   if (i64.ok()) {
188     WireFormatLite::WriteSFixed64(field_number, i64.value(), stream);
189   }
190   return i64.status();
191 }
192 
193 // Writes an SINT64 field, including tag, to the stream.
WriteSInt64(int field_number,const DataPiece & data,CodedOutputStream * stream)194 inline util::Status WriteSInt64(int field_number, const DataPiece& data,
195                                 CodedOutputStream* stream) {
196   util::StatusOr<int64_t> i64 = data.ToInt64();
197   if (i64.ok()) {
198     WireFormatLite::WriteSInt64(field_number, i64.value(), stream);
199   }
200   return i64.status();
201 }
202 
203 // Writes a FIXED64 field, including tag, to the stream.
WriteFixed64(int field_number,const DataPiece & data,CodedOutputStream * stream)204 inline util::Status WriteFixed64(int field_number, const DataPiece& data,
205                                  CodedOutputStream* stream) {
206   util::StatusOr<uint64_t> u64 = data.ToUint64();
207   if (u64.ok()) {
208     WireFormatLite::WriteFixed64(field_number, u64.value(), stream);
209   }
210   return u64.status();
211 }
212 
213 // Writes a UINT64 field, including tag, to the stream.
WriteUInt64(int field_number,const DataPiece & data,CodedOutputStream * stream)214 inline util::Status WriteUInt64(int field_number, const DataPiece& data,
215                                 CodedOutputStream* stream) {
216   util::StatusOr<uint64_t> u64 = data.ToUint64();
217   if (u64.ok()) {
218     WireFormatLite::WriteUInt64(field_number, u64.value(), stream);
219   }
220   return u64.status();
221 }
222 
223 // Writes a DOUBLE field, including tag, to the stream.
WriteDouble(int field_number,const DataPiece & data,CodedOutputStream * stream)224 inline util::Status WriteDouble(int field_number, const DataPiece& data,
225                                 CodedOutputStream* stream) {
226   util::StatusOr<double> d = data.ToDouble();
227   if (d.ok()) {
228     WireFormatLite::WriteDouble(field_number, d.value(), stream);
229   }
230   return d.status();
231 }
232 
233 // Writes a FLOAT field, including tag, to the stream.
WriteFloat(int field_number,const DataPiece & data,CodedOutputStream * stream)234 inline util::Status WriteFloat(int field_number, const DataPiece& data,
235                                CodedOutputStream* stream) {
236   util::StatusOr<float> f = data.ToFloat();
237   if (f.ok()) {
238     WireFormatLite::WriteFloat(field_number, f.value(), stream);
239   }
240   return f.status();
241 }
242 
243 // Writes a BOOL field, including tag, to the stream.
WriteBool(int field_number,const DataPiece & data,CodedOutputStream * stream)244 inline util::Status WriteBool(int field_number, const DataPiece& data,
245                               CodedOutputStream* stream) {
246   util::StatusOr<bool> b = data.ToBool();
247   if (b.ok()) {
248     WireFormatLite::WriteBool(field_number, b.value(), stream);
249   }
250   return b.status();
251 }
252 
253 // Writes a BYTES field, including tag, to the stream.
WriteBytes(int field_number,const DataPiece & data,CodedOutputStream * stream)254 inline util::Status WriteBytes(int field_number, const DataPiece& data,
255                                CodedOutputStream* stream) {
256   util::StatusOr<std::string> c = data.ToBytes();
257   if (c.ok()) {
258     WireFormatLite::WriteBytes(field_number, c.value(), stream);
259   }
260   return c.status();
261 }
262 
263 // Writes a STRING field, including tag, to the stream.
WriteString(int field_number,const DataPiece & data,CodedOutputStream * stream)264 inline util::Status WriteString(int field_number, const DataPiece& data,
265                                 CodedOutputStream* stream) {
266   util::StatusOr<std::string> s = data.ToString();
267   if (s.ok()) {
268     WireFormatLite::WriteString(field_number, s.value(), stream);
269   }
270   return s.status();
271 }
272 
273 // Given a google::protobuf::Type, returns the set of all required fields.
GetRequiredFields(const google::protobuf::Type & type)274 std::unordered_set<const google::protobuf::Field*> GetRequiredFields(
275     const google::protobuf::Type& type) {
276   std::unordered_set<const google::protobuf::Field*> required;
277   for (int i = 0; i < type.fields_size(); i++) {
278     const google::protobuf::Field& field = type.fields(i);
279     if (field.cardinality() == google::protobuf::Field::CARDINALITY_REQUIRED) {
280       required.insert(&field);
281     }
282   }
283   return required;
284 }
285 
286 }  // namespace
287 
ProtoElement(const TypeInfo * typeinfo,const google::protobuf::Type & type,ProtoWriter * enclosing)288 ProtoWriter::ProtoElement::ProtoElement(const TypeInfo* typeinfo,
289                                         const google::protobuf::Type& type,
290                                         ProtoWriter* enclosing)
291     : BaseElement(nullptr),
292       ow_(enclosing),
293       parent_field_(nullptr),
294       typeinfo_(typeinfo),
295       proto3_(type.syntax() == google::protobuf::SYNTAX_PROTO3),
296       type_(type),
297       size_index_(-1),
298       array_index_(-1),
299       // oneof_indices_ values are 1-indexed (0 means not present).
300       oneof_indices_(type.oneofs_size() + 1) {
301   if (!proto3_) {
302     required_fields_ = GetRequiredFields(type_);
303   }
304 }
305 
ProtoElement(ProtoWriter::ProtoElement * parent,const google::protobuf::Field * field,const google::protobuf::Type & type,bool is_list)306 ProtoWriter::ProtoElement::ProtoElement(ProtoWriter::ProtoElement* parent,
307                                         const google::protobuf::Field* field,
308                                         const google::protobuf::Type& type,
309                                         bool is_list)
310     : BaseElement(parent),
311       ow_(this->parent()->ow_),
312       parent_field_(field),
313       typeinfo_(this->parent()->typeinfo_),
314       proto3_(type.syntax() == google::protobuf::SYNTAX_PROTO3),
315       type_(type),
316       size_index_(!is_list &&
317                           field->kind() == google::protobuf::Field::TYPE_MESSAGE
318                       ? ow_->size_insert_.size()
319                       : -1),
320       array_index_(is_list ? 0 : -1),
321       // oneof_indices_ values are 1-indexed (0 means not present).
322       oneof_indices_(type_.oneofs_size() + 1) {
323   if (!is_list) {
324     if (ow_->IsRepeated(*field)) {
325       // Update array_index_ if it is an explicit list.
326       if (this->parent()->array_index_ >= 0) this->parent()->array_index_++;
327     } else if (!proto3_) {
328       // For required fields tracking.
329       this->parent()->RegisterField(field);
330     }
331 
332     if (field->kind() == google::protobuf::Field::TYPE_MESSAGE) {
333       if (!proto3_) {
334         required_fields_ = GetRequiredFields(type_);
335       }
336       int start_pos = ow_->stream_->ByteCount();
337       // length of serialized message is the final buffer position minus
338       // starting buffer position, plus length adjustments for size fields
339       // of any nested messages. We start with -start_pos here, so we only
340       // need to add the final buffer position to it at the end.
341       SizeInfo info = {start_pos, -start_pos};
342       ow_->size_insert_.push_back(info);
343     }
344   }
345 }
346 
pop()347 ProtoWriter::ProtoElement* ProtoWriter::ProtoElement::pop() {
348   if (!proto3_) {
349     // Calls the registered error listener for any required field(s) not yet
350     // seen.
351     for (std::unordered_set<const google::protobuf::Field*>::iterator it =
352              required_fields_.begin();
353          it != required_fields_.end(); ++it) {
354       ow_->MissingField(ow_->use_json_name_in_missing_fields_
355                             ? (*it)->json_name()
356                             : (*it)->name());
357     }
358   }
359   // Computes the total number of proto bytes used by a message, also adjusts
360   // the size of all parent messages by the length of this size field.
361   // If size_index_ < 0, this is not a message, so no size field is added.
362   if (size_index_ >= 0) {
363     // Add the final buffer position to compute the total length of this
364     // serialized message. The stored value (before this addition) already
365     // contains the total length of the size fields of all nested messages
366     // minus the initial buffer position.
367     ow_->size_insert_[size_index_].size += ow_->stream_->ByteCount();
368     // Calculate the length required to serialize the size field of the
369     // message, and propagate this additional size information upward to
370     // all enclosing messages.
371     int size = ow_->size_insert_[size_index_].size;
372     int length = CodedOutputStream::VarintSize32(size);
373     for (ProtoElement* e = parent(); e != nullptr; e = e->parent()) {
374       // Only nested messages have size field, lists do not have size field.
375       if (e->size_index_ >= 0) {
376         ow_->size_insert_[e->size_index_].size += length;
377       }
378     }
379   }
380   return BaseElement::pop<ProtoElement>();
381 }
382 
RegisterField(const google::protobuf::Field * field)383 void ProtoWriter::ProtoElement::RegisterField(
384     const google::protobuf::Field* field) {
385   if (!required_fields_.empty() &&
386       field->cardinality() == google::protobuf::Field::CARDINALITY_REQUIRED) {
387     required_fields_.erase(field);
388   }
389 }
390 
ToString() const391 std::string ProtoWriter::ProtoElement::ToString() const {
392   std::string loc = "";
393 
394   // first populate a stack with the nodes since we need to process them
395   // from root to leaf when generating the string location
396   const ProtoWriter::ProtoElement* now = this;
397   std::stack<const ProtoWriter::ProtoElement*> element_stack;
398   while (now->parent() != nullptr) {
399     element_stack.push(now);
400     now = now->parent();
401   }
402 
403   // now pop each node from the stack and append to the location string
404   while (!element_stack.empty()) {
405     now = element_stack.top();
406     element_stack.pop();
407 
408     if (!ow_->IsRepeated(*(now->parent_field_)) ||
409         now->parent()->parent_field_ != now->parent_field_) {
410       std::string name = now->parent_field_->name();
411       int i = 0;
412       while (i < name.size() &&
413              (ascii_isalnum(name[i]) || name[i] == '_'))
414         ++i;
415       if (i > 0 && i == name.size()) {  // safe field name
416         if (loc.empty()) {
417           loc = name;
418         } else {
419           StrAppend(&loc, ".", name);
420         }
421       } else {
422         StrAppend(&loc, "[\"", CEscape(name), "\"]");
423       }
424     }
425 
426     int array_index_now = now->array_index_;
427     if (ow_->IsRepeated(*(now->parent_field_)) && array_index_now > 0) {
428       StrAppend(&loc, "[", array_index_now - 1, "]");
429     }
430   }
431 
432   return loc;
433 }
434 
IsOneofIndexTaken(int32_t index)435 bool ProtoWriter::ProtoElement::IsOneofIndexTaken(int32_t index) {
436   return oneof_indices_[index];
437 }
438 
TakeOneofIndex(int32_t index)439 void ProtoWriter::ProtoElement::TakeOneofIndex(int32_t index) {
440   oneof_indices_[index] = true;
441 }
442 
InvalidName(StringPiece unknown_name,StringPiece message)443 void ProtoWriter::InvalidName(StringPiece unknown_name,
444                               StringPiece message) {
445   listener_->InvalidName(location(), unknown_name, message);
446 }
447 
InvalidValue(StringPiece type_name,StringPiece value)448 void ProtoWriter::InvalidValue(StringPiece type_name,
449                                StringPiece value) {
450   listener_->InvalidValue(location(), type_name, value);
451 }
452 
MissingField(StringPiece missing_name)453 void ProtoWriter::MissingField(StringPiece missing_name) {
454   listener_->MissingField(location(), missing_name);
455 }
456 
StartObject(StringPiece name)457 ProtoWriter* ProtoWriter::StartObject(
458     StringPiece name) {
459   // Starting the root message. Create the root ProtoElement and return.
460   if (element_ == nullptr) {
461     if (!name.empty()) {
462       InvalidName(name, "Root element should not be named.");
463     }
464     element_.reset(new ProtoElement(typeinfo_, master_type_, this));
465     return this;
466   }
467 
468   const google::protobuf::Field* field = BeginNamed(name, false);
469 
470   if (field == nullptr) return this;
471 
472   // Check to see if this field is a oneof and that no oneof in that group has
473   // already been set.
474   if (!ValidOneof(*field, name)) {
475     ++invalid_depth_;
476     return this;
477   }
478 
479   const google::protobuf::Type* type = LookupType(field);
480   if (type == nullptr) {
481     ++invalid_depth_;
482     InvalidName(name, StrCat("Missing descriptor for field: ",
483                                    field->type_url()));
484     return this;
485   }
486 
487   return StartObjectField(*field, *type);
488 }
489 
490 
EndObject()491 ProtoWriter* ProtoWriter::EndObject() {
492   if (invalid_depth_ > 0) {
493     --invalid_depth_;
494     return this;
495   }
496 
497   if (element_ != nullptr) {
498     element_.reset(element_->pop());
499   }
500 
501 
502   // If ending the root element,
503   // then serialize the full message with calculated sizes.
504   if (element_ == nullptr) {
505     WriteRootMessage();
506   }
507   return this;
508 }
509 
StartList(StringPiece name)510 ProtoWriter* ProtoWriter::StartList(
511     StringPiece name) {
512 
513   const google::protobuf::Field* field = BeginNamed(name, true);
514 
515   if (field == nullptr) return this;
516 
517   if (!ValidOneof(*field, name)) {
518     ++invalid_depth_;
519     return this;
520   }
521 
522   const google::protobuf::Type* type = LookupType(field);
523   if (type == nullptr) {
524     ++invalid_depth_;
525     InvalidName(name, StrCat("Missing descriptor for field: ",
526                                    field->type_url()));
527     return this;
528   }
529 
530   return StartListField(*field, *type);
531 }
532 
533 
EndList()534 ProtoWriter* ProtoWriter::EndList() {
535   if (invalid_depth_ > 0) {
536     --invalid_depth_;
537   } else if (element_ != nullptr) {
538     element_.reset(element_->pop());
539   }
540   return this;
541 }
542 
RenderDataPiece(StringPiece name,const DataPiece & data)543 ProtoWriter* ProtoWriter::RenderDataPiece(
544     StringPiece name, const DataPiece& data) {
545   util::Status status;
546   if (invalid_depth_ > 0) return this;
547 
548   const google::protobuf::Field* field = Lookup(name);
549 
550   if (field == nullptr) return this;
551 
552   if (!ValidOneof(*field, name)) return this;
553 
554   const google::protobuf::Type* type = LookupType(field);
555   if (type == nullptr) {
556     InvalidName(name, StrCat("Missing descriptor for field: ",
557                                    field->type_url()));
558     return this;
559   }
560 
561   return RenderPrimitiveField(*field, *type, data);
562 }
563 
ValidOneof(const google::protobuf::Field & field,StringPiece unnormalized_name)564 bool ProtoWriter::ValidOneof(const google::protobuf::Field& field,
565                              StringPiece unnormalized_name) {
566   if (element_ == nullptr) return true;
567 
568   if (field.oneof_index() > 0) {
569     if (element_->IsOneofIndexTaken(field.oneof_index())) {
570       InvalidValue(
571           "oneof",
572           StrCat(
573               "oneof field '", element_->type().oneofs(field.oneof_index() - 1),
574               "' is already set. Cannot set '", unnormalized_name, "'"));
575       return false;
576     }
577     element_->TakeOneofIndex(field.oneof_index());
578   }
579   return true;
580 }
581 
IsRepeated(const google::protobuf::Field & field)582 bool ProtoWriter::IsRepeated(const google::protobuf::Field& field) {
583   return field.cardinality() == google::protobuf::Field::CARDINALITY_REPEATED;
584 }
585 
StartObjectField(const google::protobuf::Field & field,const google::protobuf::Type & type)586 ProtoWriter* ProtoWriter::StartObjectField(const google::protobuf::Field& field,
587                                            const google::protobuf::Type& type) {
588     WriteTag(field);
589   element_.reset(new ProtoElement(element_.release(), &field, type, false));
590   return this;
591 }
592 
StartListField(const google::protobuf::Field & field,const google::protobuf::Type & type)593 ProtoWriter* ProtoWriter::StartListField(const google::protobuf::Field& field,
594                                          const google::protobuf::Type& type) {
595   element_.reset(new ProtoElement(element_.release(), &field, type, true));
596   return this;
597 }
598 
WriteEnum(int field_number,const DataPiece & data,const google::protobuf::Enum * enum_type,CodedOutputStream * stream,bool use_lower_camel_for_enums,bool case_insensitive_enum_parsing,bool ignore_unknown_values)599 util::Status ProtoWriter::WriteEnum(int field_number, const DataPiece& data,
600                                     const google::protobuf::Enum* enum_type,
601                                     CodedOutputStream* stream,
602                                     bool use_lower_camel_for_enums,
603                                     bool case_insensitive_enum_parsing,
604                                     bool ignore_unknown_values) {
605   bool is_unknown_enum_value = false;
606   util::StatusOr<int> e = data.ToEnum(
607       enum_type, use_lower_camel_for_enums, case_insensitive_enum_parsing,
608       ignore_unknown_values, &is_unknown_enum_value);
609   if (e.ok() && !is_unknown_enum_value) {
610     WireFormatLite::WriteEnum(field_number, e.value(), stream);
611   }
612   return e.status();
613 }
614 
RenderPrimitiveField(const google::protobuf::Field & field,const google::protobuf::Type & type,const DataPiece & data)615 ProtoWriter* ProtoWriter::RenderPrimitiveField(
616     const google::protobuf::Field& field, const google::protobuf::Type& type,
617     const DataPiece& data) {
618   util::Status status;
619 
620   // Pushing a ProtoElement and then pop it off at the end for 2 purposes:
621   // error location reporting and required field accounting.
622   //
623   // For proto3, since there is no required field tracking, we only need to
624   // push ProtoElement for error cases.
625   if (!element_->proto3()) {
626     element_.reset(new ProtoElement(element_.release(), &field, type, false));
627   }
628 
629   switch (field.kind()) {
630     case google::protobuf::Field::TYPE_INT32: {
631       status = WriteInt32(field.number(), data, stream_.get());
632       break;
633     }
634     case google::protobuf::Field::TYPE_SFIXED32: {
635       status = WriteSFixed32(field.number(), data, stream_.get());
636       break;
637     }
638     case google::protobuf::Field::TYPE_SINT32: {
639       status = WriteSInt32(field.number(), data, stream_.get());
640       break;
641     }
642     case google::protobuf::Field::TYPE_FIXED32: {
643       status = WriteFixed32(field.number(), data, stream_.get());
644       break;
645     }
646     case google::protobuf::Field::TYPE_UINT32: {
647       status = WriteUInt32(field.number(), data, stream_.get());
648       break;
649     }
650     case google::protobuf::Field::TYPE_INT64: {
651       status = WriteInt64(field.number(), data, stream_.get());
652       break;
653     }
654     case google::protobuf::Field::TYPE_SFIXED64: {
655       status = WriteSFixed64(field.number(), data, stream_.get());
656       break;
657     }
658     case google::protobuf::Field::TYPE_SINT64: {
659       status = WriteSInt64(field.number(), data, stream_.get());
660       break;
661     }
662     case google::protobuf::Field::TYPE_FIXED64: {
663       status = WriteFixed64(field.number(), data, stream_.get());
664       break;
665     }
666     case google::protobuf::Field::TYPE_UINT64: {
667       status = WriteUInt64(field.number(), data, stream_.get());
668       break;
669     }
670     case google::protobuf::Field::TYPE_DOUBLE: {
671       status = WriteDouble(field.number(), data, stream_.get());
672       break;
673     }
674     case google::protobuf::Field::TYPE_FLOAT: {
675       status = WriteFloat(field.number(), data, stream_.get());
676       break;
677     }
678     case google::protobuf::Field::TYPE_BOOL: {
679       status = WriteBool(field.number(), data, stream_.get());
680       break;
681     }
682     case google::protobuf::Field::TYPE_BYTES: {
683       status = WriteBytes(field.number(), data, stream_.get());
684       break;
685     }
686     case google::protobuf::Field::TYPE_STRING: {
687       status = WriteString(field.number(), data, stream_.get());
688       break;
689     }
690     case google::protobuf::Field::TYPE_ENUM: {
691       status = WriteEnum(
692           field.number(), data, typeinfo_->GetEnumByTypeUrl(field.type_url()),
693           stream_.get(), use_lower_camel_for_enums_,
694           case_insensitive_enum_parsing_, ignore_unknown_enum_values_);
695       break;
696     }
697     default:  // TYPE_GROUP, TYPE_MESSAGE, TYPE_UNKNOWN.
698       status = util::InvalidArgumentError(data.ValueAsStringOrDefault(""));
699   }
700 
701   if (!status.ok()) {
702     // Push a ProtoElement for location reporting purposes.
703     if (element_->proto3()) {
704       element_.reset(new ProtoElement(element_.release(), &field, type, false));
705     }
706     InvalidValue(field.type_url().empty()
707                      ? google::protobuf::Field_Kind_Name(field.kind())
708                      : field.type_url(),
709                  status.message());
710     element_.reset(element()->pop());
711     return this;
712   }
713 
714   if (!element_->proto3()) element_.reset(element()->pop());
715 
716   return this;
717 }
718 
BeginNamed(StringPiece name,bool is_list)719 const google::protobuf::Field* ProtoWriter::BeginNamed(StringPiece name,
720                                                        bool is_list) {
721   if (invalid_depth_ > 0) {
722     ++invalid_depth_;
723     return nullptr;
724   }
725   const google::protobuf::Field* field = Lookup(name);
726   if (field == nullptr) {
727     ++invalid_depth_;
728     // InvalidName() already called in Lookup().
729     return nullptr;
730   }
731   if (is_list && !IsRepeated(*field)) {
732     ++invalid_depth_;
733     InvalidName(name, "Proto field is not repeating, cannot start list.");
734     return nullptr;
735   }
736   return field;
737 }
738 
Lookup(StringPiece unnormalized_name)739 const google::protobuf::Field* ProtoWriter::Lookup(
740     StringPiece unnormalized_name) {
741   ProtoElement* e = element();
742   if (e == nullptr) {
743     InvalidName(unnormalized_name, "Root element must be a message.");
744     return nullptr;
745   }
746   if (unnormalized_name.empty()) {
747     // Objects in repeated field inherit the same field descriptor.
748     if (e->parent_field() == nullptr) {
749       InvalidName(unnormalized_name, "Proto fields must have a name.");
750     } else if (!IsRepeated(*e->parent_field())) {
751       InvalidName(unnormalized_name, "Proto fields must have a name.");
752       return nullptr;
753     }
754     return e->parent_field();
755   }
756   const google::protobuf::Field* field =
757       typeinfo_->FindField(&e->type(), unnormalized_name);
758   if (field == nullptr && !ignore_unknown_fields_) {
759     InvalidName(unnormalized_name, "Cannot find field.");
760   }
761   return field;
762 }
763 
LookupType(const google::protobuf::Field * field)764 const google::protobuf::Type* ProtoWriter::LookupType(
765     const google::protobuf::Field* field) {
766   return ((field->kind() == google::protobuf::Field::TYPE_MESSAGE ||
767            field->kind() == google::protobuf::Field::TYPE_GROUP)
768               ? typeinfo_->GetTypeByTypeUrl(field->type_url())
769               : &element_->type());
770 }
771 
WriteRootMessage()772 void ProtoWriter::WriteRootMessage() {
773   GOOGLE_DCHECK(!done_);
774   int curr_pos = 0;
775   // Calls the destructor of CodedOutputStream to remove any uninitialized
776   // memory from the Cord before we read it.
777   stream_.reset(nullptr);
778   const void* data;
779   int length;
780   io::ArrayInputStream input_stream(buffer_.data(), buffer_.size());
781   while (input_stream.Next(&data, &length)) {
782     if (length == 0) continue;
783     int num_bytes = length;
784     // Write up to where we need to insert the size field.
785     // The number of bytes we may write is the smaller of:
786     //   - the current fragment size
787     //   - the distance to the next position where a size field needs to be
788     //     inserted.
789     if (!size_insert_.empty() &&
790         size_insert_.front().pos - curr_pos < num_bytes) {
791       num_bytes = size_insert_.front().pos - curr_pos;
792     }
793     output_->Append(static_cast<const char*>(data), num_bytes);
794     if (num_bytes < length) {
795       input_stream.BackUp(length - num_bytes);
796     }
797     curr_pos += num_bytes;
798     // Insert the size field.
799     //   size_insert_.front():      the next <index, size> pair to be written.
800     //   size_insert_.front().pos:  position of the size field.
801     //   size_insert_.front().size: the size (integer) to be inserted.
802     if (!size_insert_.empty() && curr_pos == size_insert_.front().pos) {
803       // Varint32 occupies at most 10 bytes.
804       uint8_t insert_buffer[10];
805       uint8_t* insert_buffer_pos = CodedOutputStream::WriteVarint32ToArray(
806           size_insert_.front().size, insert_buffer);
807       output_->Append(reinterpret_cast<const char*>(insert_buffer),
808                       insert_buffer_pos - insert_buffer);
809       size_insert_.pop_front();
810     }
811   }
812   output_->Flush();
813   stream_.reset(new CodedOutputStream(&adapter_));
814   done_ = true;
815 }
816 
WriteTag(const google::protobuf::Field & field)817 void ProtoWriter::WriteTag(const google::protobuf::Field& field) {
818   WireFormatLite::WireType wire_type = WireFormatLite::WireTypeForFieldType(
819       static_cast<WireFormatLite::FieldType>(field.kind()));
820   stream_->WriteTag(WireFormatLite::MakeTag(field.number(), wire_type));
821 }
822 
823 
824 }  // namespace converter
825 }  // namespace util
826 }  // namespace protobuf
827 }  // namespace google
828