• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 #include <google/protobuf/util/internal/proto_writer.h>
32 
33 #include <functional>
34 #include <stack>
35 
36 #include <google/protobuf/stubs/once.h>
37 #include <google/protobuf/stubs/time.h>
38 #include <google/protobuf/wire_format_lite.h>
39 #include <google/protobuf/util/internal/field_mask_utility.h>
40 #include <google/protobuf/util/internal/object_location_tracker.h>
41 #include <google/protobuf/util/internal/constants.h>
42 #include <google/protobuf/util/internal/utility.h>
43 #include <google/protobuf/stubs/strutil.h>
44 #include <google/protobuf/stubs/map_util.h>
45 #include <google/protobuf/stubs/statusor.h>
46 
47 
48 #include <google/protobuf/port_def.inc>
49 
50 namespace google {
51 namespace protobuf {
52 namespace util {
53 namespace converter {
54 
55 using io::CodedOutputStream;
56 using ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite;
57 using util::Status;
58 using util::StatusOr;
59 using util::error::INVALID_ARGUMENT;
60 
61 
ProtoWriter(TypeResolver * type_resolver,const google::protobuf::Type & type,strings::ByteSink * output,ErrorListener * listener)62 ProtoWriter::ProtoWriter(TypeResolver* type_resolver,
63                          const google::protobuf::Type& type,
64                          strings::ByteSink* output, ErrorListener* listener)
65     : master_type_(type),
66       typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
67       own_typeinfo_(true),
68       done_(false),
69       ignore_unknown_fields_(false),
70       ignore_unknown_enum_values_(false),
71       use_lower_camel_for_enums_(false),
72       case_insensitive_enum_parsing_(true),
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       element_(nullptr),
95       size_insert_(),
96       output_(output),
97       buffer_(),
98       adapter_(&buffer_),
99       stream_(new CodedOutputStream(&adapter_)),
100       listener_(listener),
101       invalid_depth_(0),
102       tracker_(new ObjectLocationTracker()) {}
103 
~ProtoWriter()104 ProtoWriter::~ProtoWriter() {
105   if (own_typeinfo_) {
106     delete typeinfo_;
107   }
108   if (element_ == nullptr) return;
109   // Cleanup explicitly in order to avoid destructor stack overflow when input
110   // is deeply nested.
111   // Cast to BaseElement to avoid doing additional checks (like missing fields)
112   // during pop().
113   std::unique_ptr<BaseElement> element(
114       static_cast<BaseElement*>(element_.get())->pop<BaseElement>());
115   while (element != nullptr) {
116     element.reset(element->pop<BaseElement>());
117   }
118 }
119 
120 namespace {
121 
122 // Writes an INT32 field, including tag to the stream.
WriteInt32(int field_number,const DataPiece & data,CodedOutputStream * stream)123 inline Status WriteInt32(int field_number, const DataPiece& data,
124                          CodedOutputStream* stream) {
125   StatusOr<int32> i32 = data.ToInt32();
126   if (i32.ok()) {
127     WireFormatLite::WriteInt32(field_number, i32.ValueOrDie(), stream);
128   }
129   return i32.status();
130 }
131 
132 // writes an SFIXED32 field, including tag, to the stream.
WriteSFixed32(int field_number,const DataPiece & data,CodedOutputStream * stream)133 inline Status WriteSFixed32(int field_number, const DataPiece& data,
134                             CodedOutputStream* stream) {
135   StatusOr<int32> i32 = data.ToInt32();
136   if (i32.ok()) {
137     WireFormatLite::WriteSFixed32(field_number, i32.ValueOrDie(), stream);
138   }
139   return i32.status();
140 }
141 
142 // Writes an SINT32 field, including tag, to the stream.
WriteSInt32(int field_number,const DataPiece & data,CodedOutputStream * stream)143 inline Status WriteSInt32(int field_number, const DataPiece& data,
144                           CodedOutputStream* stream) {
145   StatusOr<int32> i32 = data.ToInt32();
146   if (i32.ok()) {
147     WireFormatLite::WriteSInt32(field_number, i32.ValueOrDie(), stream);
148   }
149   return i32.status();
150 }
151 
152 // Writes a FIXED32 field, including tag, to the stream.
WriteFixed32(int field_number,const DataPiece & data,CodedOutputStream * stream)153 inline Status WriteFixed32(int field_number, const DataPiece& data,
154                            CodedOutputStream* stream) {
155   StatusOr<uint32> u32 = data.ToUint32();
156   if (u32.ok()) {
157     WireFormatLite::WriteFixed32(field_number, u32.ValueOrDie(), stream);
158   }
159   return u32.status();
160 }
161 
162 // Writes a UINT32 field, including tag, to the stream.
WriteUInt32(int field_number,const DataPiece & data,CodedOutputStream * stream)163 inline Status WriteUInt32(int field_number, const DataPiece& data,
164                           CodedOutputStream* stream) {
165   StatusOr<uint32> u32 = data.ToUint32();
166   if (u32.ok()) {
167     WireFormatLite::WriteUInt32(field_number, u32.ValueOrDie(), stream);
168   }
169   return u32.status();
170 }
171 
172 // Writes an INT64 field, including tag, to the stream.
WriteInt64(int field_number,const DataPiece & data,CodedOutputStream * stream)173 inline Status WriteInt64(int field_number, const DataPiece& data,
174                          CodedOutputStream* stream) {
175   StatusOr<int64> i64 = data.ToInt64();
176   if (i64.ok()) {
177     WireFormatLite::WriteInt64(field_number, i64.ValueOrDie(), stream);
178   }
179   return i64.status();
180 }
181 
182 // Writes an SFIXED64 field, including tag, to the stream.
WriteSFixed64(int field_number,const DataPiece & data,CodedOutputStream * stream)183 inline Status WriteSFixed64(int field_number, const DataPiece& data,
184                             CodedOutputStream* stream) {
185   StatusOr<int64> i64 = data.ToInt64();
186   if (i64.ok()) {
187     WireFormatLite::WriteSFixed64(field_number, i64.ValueOrDie(), stream);
188   }
189   return i64.status();
190 }
191 
192 // Writes an SINT64 field, including tag, to the stream.
WriteSInt64(int field_number,const DataPiece & data,CodedOutputStream * stream)193 inline Status WriteSInt64(int field_number, const DataPiece& data,
194                           CodedOutputStream* stream) {
195   StatusOr<int64> i64 = data.ToInt64();
196   if (i64.ok()) {
197     WireFormatLite::WriteSInt64(field_number, i64.ValueOrDie(), stream);
198   }
199   return i64.status();
200 }
201 
202 // Writes a FIXED64 field, including tag, to the stream.
WriteFixed64(int field_number,const DataPiece & data,CodedOutputStream * stream)203 inline Status WriteFixed64(int field_number, const DataPiece& data,
204                            CodedOutputStream* stream) {
205   StatusOr<uint64> u64 = data.ToUint64();
206   if (u64.ok()) {
207     WireFormatLite::WriteFixed64(field_number, u64.ValueOrDie(), stream);
208   }
209   return u64.status();
210 }
211 
212 // Writes a UINT64 field, including tag, to the stream.
WriteUInt64(int field_number,const DataPiece & data,CodedOutputStream * stream)213 inline Status WriteUInt64(int field_number, const DataPiece& data,
214                           CodedOutputStream* stream) {
215   StatusOr<uint64> u64 = data.ToUint64();
216   if (u64.ok()) {
217     WireFormatLite::WriteUInt64(field_number, u64.ValueOrDie(), stream);
218   }
219   return u64.status();
220 }
221 
222 // Writes a DOUBLE field, including tag, to the stream.
WriteDouble(int field_number,const DataPiece & data,CodedOutputStream * stream)223 inline Status WriteDouble(int field_number, const DataPiece& data,
224                           CodedOutputStream* stream) {
225   StatusOr<double> d = data.ToDouble();
226   if (d.ok()) {
227     WireFormatLite::WriteDouble(field_number, d.ValueOrDie(), stream);
228   }
229   return d.status();
230 }
231 
232 // Writes a FLOAT field, including tag, to the stream.
WriteFloat(int field_number,const DataPiece & data,CodedOutputStream * stream)233 inline Status WriteFloat(int field_number, const DataPiece& data,
234                          CodedOutputStream* stream) {
235   StatusOr<float> f = data.ToFloat();
236   if (f.ok()) {
237     WireFormatLite::WriteFloat(field_number, f.ValueOrDie(), stream);
238   }
239   return f.status();
240 }
241 
242 // Writes a BOOL field, including tag, to the stream.
WriteBool(int field_number,const DataPiece & data,CodedOutputStream * stream)243 inline Status WriteBool(int field_number, const DataPiece& data,
244                         CodedOutputStream* stream) {
245   StatusOr<bool> b = data.ToBool();
246   if (b.ok()) {
247     WireFormatLite::WriteBool(field_number, b.ValueOrDie(), stream);
248   }
249   return b.status();
250 }
251 
252 // Writes a BYTES field, including tag, to the stream.
WriteBytes(int field_number,const DataPiece & data,CodedOutputStream * stream)253 inline Status WriteBytes(int field_number, const DataPiece& data,
254                          CodedOutputStream* stream) {
255   StatusOr<std::string> c = data.ToBytes();
256   if (c.ok()) {
257     WireFormatLite::WriteBytes(field_number, c.ValueOrDie(), stream);
258   }
259   return c.status();
260 }
261 
262 // Writes a STRING field, including tag, to the stream.
WriteString(int field_number,const DataPiece & data,CodedOutputStream * stream)263 inline Status WriteString(int field_number, const DataPiece& data,
264                           CodedOutputStream* stream) {
265   StatusOr<std::string> s = data.ToString();
266   if (s.ok()) {
267     WireFormatLite::WriteString(field_number, s.ValueOrDie(), stream);
268   }
269   return s.status();
270 }
271 
272 // Given a google::protobuf::Type, returns the set of all required fields.
GetRequiredFields(const google::protobuf::Type & type)273 std::set<const google::protobuf::Field*> GetRequiredFields(
274     const google::protobuf::Type& type) {
275   std::set<const google::protobuf::Field*> required;
276   for (int i = 0; i < type.fields_size(); i++) {
277     const google::protobuf::Field& field = type.fields(i);
278     if (field.cardinality() ==
279         google::protobuf::Field_Cardinality_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 && field->kind() ==
317                                   google::protobuf::Field_Kind_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_Kind_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::set<const google::protobuf::Field*>::iterator it =
352              required_fields_.begin();
353          it != required_fields_.end(); ++it) {
354       ow_->MissingField((*it)->name());
355     }
356   }
357   // Computes the total number of proto bytes used by a message, also adjusts
358   // the size of all parent messages by the length of this size field.
359   // If size_index_ < 0, this is not a message, so no size field is added.
360   if (size_index_ >= 0) {
361     // Add the final buffer position to compute the total length of this
362     // serialized message. The stored value (before this addition) already
363     // contains the total length of the size fields of all nested messages
364     // minus the initial buffer position.
365     ow_->size_insert_[size_index_].size += ow_->stream_->ByteCount();
366     // Calculate the length required to serialize the size field of the
367     // message, and propagate this additional size information upward to
368     // all enclosing messages.
369     int size = ow_->size_insert_[size_index_].size;
370     int length = CodedOutputStream::VarintSize32(size);
371     for (ProtoElement* e = parent(); e != nullptr; e = e->parent()) {
372       // Only nested messages have size field, lists do not have size field.
373       if (e->size_index_ >= 0) {
374         ow_->size_insert_[e->size_index_].size += length;
375       }
376     }
377   }
378   return BaseElement::pop<ProtoElement>();
379 }
380 
RegisterField(const google::protobuf::Field * field)381 void ProtoWriter::ProtoElement::RegisterField(
382     const google::protobuf::Field* field) {
383   if (!required_fields_.empty() &&
384       field->cardinality() ==
385           google::protobuf::Field_Cardinality_CARDINALITY_REQUIRED) {
386     required_fields_.erase(field);
387   }
388 }
389 
ToString() const390 std::string ProtoWriter::ProtoElement::ToString() const {
391   std::string loc = "";
392 
393   // first populate a stack with the nodes since we need to process them
394   // from root to leaf when generating the string location
395   const ProtoWriter::ProtoElement* now = this;
396   std::stack<const ProtoWriter::ProtoElement*> element_stack;
397   while (now->parent() != nullptr) {
398     element_stack.push(now);
399     now = now->parent();
400   }
401 
402   // now pop each node from the stack and append to the location string
403   while (!element_stack.empty()) {
404     now = element_stack.top();
405     element_stack.pop();
406 
407     if (!ow_->IsRepeated(*(now->parent_field_)) ||
408         now->parent()->parent_field_ != now->parent_field_) {
409       std::string name = now->parent_field_->name();
410       int i = 0;
411       while (i < name.size() && (ascii_isalnum(name[i]) || name[i] == '_')) ++i;
412       if (i > 0 && i == name.size()) {  // safe field name
413         if (loc.empty()) {
414           loc = name;
415         } else {
416           StrAppend(&loc, ".", name);
417         }
418       } else {
419         StrAppend(&loc, "[\"", CEscape(name), "\"]");
420       }
421     }
422 
423     int array_index_now = now->array_index_;
424     if (ow_->IsRepeated(*(now->parent_field_)) && array_index_now > 0) {
425       StrAppend(&loc, "[", array_index_now - 1, "]");
426     }
427   }
428 
429   return loc;
430 }
431 
IsOneofIndexTaken(int32 index)432 bool ProtoWriter::ProtoElement::IsOneofIndexTaken(int32 index) {
433   return oneof_indices_[index];
434 }
435 
TakeOneofIndex(int32 index)436 void ProtoWriter::ProtoElement::TakeOneofIndex(int32 index) {
437   oneof_indices_[index] = true;
438 }
439 
InvalidName(StringPiece unknown_name,StringPiece message)440 void ProtoWriter::InvalidName(StringPiece unknown_name,
441                               StringPiece message) {
442   listener_->InvalidName(location(), ToSnakeCase(unknown_name), message);
443 }
444 
InvalidValue(StringPiece type_name,StringPiece value)445 void ProtoWriter::InvalidValue(StringPiece type_name,
446                                StringPiece value) {
447   listener_->InvalidValue(location(), type_name, value);
448 }
449 
MissingField(StringPiece missing_name)450 void ProtoWriter::MissingField(StringPiece missing_name) {
451   listener_->MissingField(location(), missing_name);
452 }
453 
StartObject(StringPiece name)454 ProtoWriter* ProtoWriter::StartObject(StringPiece name) {
455   // Starting the root message. Create the root ProtoElement and return.
456   if (element_ == nullptr) {
457     if (!name.empty()) {
458       InvalidName(name, "Root element should not be named.");
459     }
460     element_.reset(new ProtoElement(typeinfo_, master_type_, this));
461     return this;
462   }
463 
464   const google::protobuf::Field* field = nullptr;
465   field = BeginNamed(name, false);
466   if (field == nullptr) return this;
467 
468   // Check to see if this field is a oneof and that no oneof in that group has
469   // already been set.
470   if (!ValidOneof(*field, name)) {
471     ++invalid_depth_;
472     return this;
473   }
474 
475   const google::protobuf::Type* type = LookupType(field);
476   if (type == nullptr) {
477     ++invalid_depth_;
478     InvalidName(name, StrCat("Missing descriptor for field: ",
479                                    field->type_url()));
480     return this;
481   }
482 
483   return StartObjectField(*field, *type);
484 }
485 
EndObject()486 ProtoWriter* ProtoWriter::EndObject() {
487   if (invalid_depth_ > 0) {
488     --invalid_depth_;
489     return this;
490   }
491 
492   if (element_ != nullptr) {
493     element_.reset(element_->pop());
494   }
495 
496 
497   // If ending the root element,
498   // then serialize the full message with calculated sizes.
499   if (element_ == nullptr) {
500     WriteRootMessage();
501   }
502   return this;
503 }
504 
StartList(StringPiece name)505 ProtoWriter* ProtoWriter::StartList(StringPiece name) {
506   const google::protobuf::Field* field = BeginNamed(name, true);
507   if (field == nullptr) return this;
508 
509   if (!ValidOneof(*field, name)) {
510     ++invalid_depth_;
511     return this;
512   }
513 
514   const google::protobuf::Type* type = LookupType(field);
515   if (type == nullptr) {
516     ++invalid_depth_;
517     InvalidName(name, StrCat("Missing descriptor for field: ",
518                                    field->type_url()));
519     return this;
520   }
521 
522   return StartListField(*field, *type);
523 }
524 
EndList()525 ProtoWriter* ProtoWriter::EndList() {
526   if (invalid_depth_ > 0) {
527     --invalid_depth_;
528   } else if (element_ != nullptr) {
529     element_.reset(element_->pop());
530   }
531   return this;
532 }
533 
RenderDataPiece(StringPiece name,const DataPiece & data)534 ProtoWriter* ProtoWriter::RenderDataPiece(StringPiece name,
535                                           const DataPiece& data) {
536   Status status;
537   if (invalid_depth_ > 0) return this;
538 
539   const google::protobuf::Field* field = Lookup(name);
540   if (field == nullptr) return this;
541 
542   if (!ValidOneof(*field, name)) return this;
543 
544   const google::protobuf::Type* type = LookupType(field);
545   if (type == nullptr) {
546     InvalidName(name, StrCat("Missing descriptor for field: ",
547                                    field->type_url()));
548     return this;
549   }
550 
551   return RenderPrimitiveField(*field, *type, data);
552 }
553 
ValidOneof(const google::protobuf::Field & field,StringPiece unnormalized_name)554 bool ProtoWriter::ValidOneof(const google::protobuf::Field& field,
555                              StringPiece unnormalized_name) {
556   if (element_ == nullptr) return true;
557 
558   if (field.oneof_index() > 0) {
559     if (element_->IsOneofIndexTaken(field.oneof_index())) {
560       InvalidValue(
561           "oneof",
562           StrCat(
563               "oneof field '", element_->type().oneofs(field.oneof_index() - 1),
564               "' is already set. Cannot set '", unnormalized_name, "'"));
565       return false;
566     }
567     element_->TakeOneofIndex(field.oneof_index());
568   }
569   return true;
570 }
571 
IsRepeated(const google::protobuf::Field & field)572 bool ProtoWriter::IsRepeated(const google::protobuf::Field& field) {
573   return field.cardinality() ==
574          google::protobuf::Field_Cardinality_CARDINALITY_REPEATED;
575 }
576 
StartObjectField(const google::protobuf::Field & field,const google::protobuf::Type & type)577 ProtoWriter* ProtoWriter::StartObjectField(const google::protobuf::Field& field,
578                                            const google::protobuf::Type& type) {
579     WriteTag(field);
580   element_.reset(new ProtoElement(element_.release(), &field, type, false));
581   return this;
582 }
583 
StartListField(const google::protobuf::Field & field,const google::protobuf::Type & type)584 ProtoWriter* ProtoWriter::StartListField(const google::protobuf::Field& field,
585                                          const google::protobuf::Type& type) {
586   element_.reset(new ProtoElement(element_.release(), &field, type, true));
587   return this;
588 }
589 
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)590 Status ProtoWriter::WriteEnum(int field_number, const DataPiece& data,
591                               const google::protobuf::Enum* enum_type,
592                               CodedOutputStream* stream,
593                               bool use_lower_camel_for_enums,
594                               bool case_insensitive_enum_parsing,
595                               bool ignore_unknown_values) {
596   bool is_unknown_enum_value = false;
597   StatusOr<int> e = data.ToEnum(enum_type, use_lower_camel_for_enums,
598                                 case_insensitive_enum_parsing,
599                                 ignore_unknown_values, &is_unknown_enum_value);
600   if (e.ok() && !is_unknown_enum_value) {
601     WireFormatLite::WriteEnum(field_number, e.ValueOrDie(), stream);
602   }
603   return e.status();
604 }
605 
RenderPrimitiveField(const google::protobuf::Field & field,const google::protobuf::Type & type,const DataPiece & data)606 ProtoWriter* ProtoWriter::RenderPrimitiveField(
607     const google::protobuf::Field& field, const google::protobuf::Type& type,
608     const DataPiece& data) {
609   Status status;
610 
611   // Pushing a ProtoElement and then pop it off at the end for 2 purposes:
612   // error location reporting and required field accounting.
613   //
614   // For proto3, since there is no required field tracking, we only need to push
615   // ProtoElement for error cases.
616   if (!element_->proto3()) {
617     element_.reset(new ProtoElement(element_.release(), &field, type, false));
618   }
619 
620   if (field.kind() == google::protobuf::Field_Kind_TYPE_UNKNOWN ||
621       field.kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) {
622     // Push a ProtoElement for location reporting purposes.
623     if (element_->proto3()) {
624       element_.reset(new ProtoElement(element_.release(), &field, type, false));
625     }
626     InvalidValue(field.type_url().empty()
627                      ? google::protobuf::Field_Kind_Name(field.kind())
628                      : field.type_url(),
629                  data.ValueAsStringOrDefault(""));
630     element_.reset(element()->pop());
631     return this;
632   }
633 
634   switch (field.kind()) {
635     case google::protobuf::Field_Kind_TYPE_INT32: {
636       status = WriteInt32(field.number(), data, stream_.get());
637       break;
638     }
639     case google::protobuf::Field_Kind_TYPE_SFIXED32: {
640       status = WriteSFixed32(field.number(), data, stream_.get());
641       break;
642     }
643     case google::protobuf::Field_Kind_TYPE_SINT32: {
644       status = WriteSInt32(field.number(), data, stream_.get());
645       break;
646     }
647     case google::protobuf::Field_Kind_TYPE_FIXED32: {
648       status = WriteFixed32(field.number(), data, stream_.get());
649       break;
650     }
651     case google::protobuf::Field_Kind_TYPE_UINT32: {
652       status = WriteUInt32(field.number(), data, stream_.get());
653       break;
654     }
655     case google::protobuf::Field_Kind_TYPE_INT64: {
656       status = WriteInt64(field.number(), data, stream_.get());
657       break;
658     }
659     case google::protobuf::Field_Kind_TYPE_SFIXED64: {
660       status = WriteSFixed64(field.number(), data, stream_.get());
661       break;
662     }
663     case google::protobuf::Field_Kind_TYPE_SINT64: {
664       status = WriteSInt64(field.number(), data, stream_.get());
665       break;
666     }
667     case google::protobuf::Field_Kind_TYPE_FIXED64: {
668       status = WriteFixed64(field.number(), data, stream_.get());
669       break;
670     }
671     case google::protobuf::Field_Kind_TYPE_UINT64: {
672       status = WriteUInt64(field.number(), data, stream_.get());
673       break;
674     }
675     case google::protobuf::Field_Kind_TYPE_DOUBLE: {
676       status = WriteDouble(field.number(), data, stream_.get());
677       break;
678     }
679     case google::protobuf::Field_Kind_TYPE_FLOAT: {
680       status = WriteFloat(field.number(), data, stream_.get());
681       break;
682     }
683     case google::protobuf::Field_Kind_TYPE_BOOL: {
684       status = WriteBool(field.number(), data, stream_.get());
685       break;
686     }
687     case google::protobuf::Field_Kind_TYPE_BYTES: {
688       status = WriteBytes(field.number(), data, stream_.get());
689       break;
690     }
691     case google::protobuf::Field_Kind_TYPE_STRING: {
692       status = WriteString(field.number(), data, stream_.get());
693       break;
694     }
695     case google::protobuf::Field_Kind_TYPE_ENUM: {
696       status = WriteEnum(
697           field.number(), data, typeinfo_->GetEnumByTypeUrl(field.type_url()),
698           stream_.get(), use_lower_camel_for_enums_,
699           case_insensitive_enum_parsing_, ignore_unknown_enum_values_);
700       break;
701     }
702     default:  // TYPE_GROUP or TYPE_MESSAGE
703       status = Status(util::error::INVALID_ARGUMENT,
704                       data.ToString().ValueOrDie());
705   }
706 
707   if (!status.ok()) {
708     // Push a ProtoElement for location reporting purposes.
709     if (element_->proto3()) {
710       element_.reset(new ProtoElement(element_.release(), &field, type, false));
711     }
712     InvalidValue(google::protobuf::Field_Kind_Name(field.kind()),
713                  status.message());
714     element_.reset(element()->pop());
715     return this;
716   }
717 
718   if (!element_->proto3()) element_.reset(element()->pop());
719 
720   return this;
721 }
722 
BeginNamed(StringPiece name,bool is_list)723 const google::protobuf::Field* ProtoWriter::BeginNamed(StringPiece name,
724                                                        bool is_list) {
725   if (invalid_depth_ > 0) {
726     ++invalid_depth_;
727     return nullptr;
728   }
729   const google::protobuf::Field* field = Lookup(name);
730   if (field == nullptr) {
731     ++invalid_depth_;
732     // InvalidName() already called in Lookup().
733     return nullptr;
734   }
735   if (is_list && !IsRepeated(*field)) {
736     ++invalid_depth_;
737     InvalidName(name, "Proto field is not repeating, cannot start list.");
738     return nullptr;
739   }
740   return field;
741 }
742 
Lookup(StringPiece unnormalized_name)743 const google::protobuf::Field* ProtoWriter::Lookup(
744     StringPiece unnormalized_name) {
745   ProtoElement* e = element();
746   if (e == nullptr) {
747     InvalidName(unnormalized_name, "Root element must be a message.");
748     return nullptr;
749   }
750   if (unnormalized_name.empty()) {
751     // Objects in repeated field inherit the same field descriptor.
752     if (e->parent_field() == nullptr) {
753       InvalidName(unnormalized_name, "Proto fields must have a name.");
754     } else if (!IsRepeated(*e->parent_field())) {
755       InvalidName(unnormalized_name, "Proto fields must have a name.");
756       return nullptr;
757     }
758     return e->parent_field();
759   }
760   const google::protobuf::Field* field =
761       typeinfo_->FindField(&e->type(), unnormalized_name);
762   if (field == nullptr && !ignore_unknown_fields_) {
763     InvalidName(unnormalized_name, "Cannot find field.");
764   }
765   return field;
766 }
767 
LookupType(const google::protobuf::Field * field)768 const google::protobuf::Type* ProtoWriter::LookupType(
769     const google::protobuf::Field* field) {
770   return ((field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE ||
771            field->kind() == google::protobuf::Field_Kind_TYPE_GROUP)
772               ? typeinfo_->GetTypeByTypeUrl(field->type_url())
773               : &element_->type());
774 }
775 
WriteRootMessage()776 void ProtoWriter::WriteRootMessage() {
777   GOOGLE_DCHECK(!done_);
778   int curr_pos = 0;
779   // Calls the destructor of CodedOutputStream to remove any uninitialized
780   // memory from the Cord before we read it.
781   stream_.reset(nullptr);
782   const void* data;
783   int length;
784   io::ArrayInputStream input_stream(buffer_.data(), buffer_.size());
785   while (input_stream.Next(&data, &length)) {
786     if (length == 0) continue;
787     int num_bytes = length;
788     // Write up to where we need to insert the size field.
789     // The number of bytes we may write is the smaller of:
790     //   - the current fragment size
791     //   - the distance to the next position where a size field needs to be
792     //     inserted.
793     if (!size_insert_.empty() &&
794         size_insert_.front().pos - curr_pos < num_bytes) {
795       num_bytes = size_insert_.front().pos - curr_pos;
796     }
797     output_->Append(static_cast<const char*>(data), num_bytes);
798     if (num_bytes < length) {
799       input_stream.BackUp(length - num_bytes);
800     }
801     curr_pos += num_bytes;
802     // Insert the size field.
803     //   size_insert_.front():      the next <index, size> pair to be written.
804     //   size_insert_.front().pos:  position of the size field.
805     //   size_insert_.front().size: the size (integer) to be inserted.
806     if (!size_insert_.empty() && curr_pos == size_insert_.front().pos) {
807       // Varint32 occupies at most 10 bytes.
808       uint8 insert_buffer[10];
809       uint8* insert_buffer_pos = CodedOutputStream::WriteVarint32ToArray(
810           size_insert_.front().size, insert_buffer);
811       output_->Append(reinterpret_cast<const char*>(insert_buffer),
812                       insert_buffer_pos - insert_buffer);
813       size_insert_.pop_front();
814     }
815   }
816   output_->Flush();
817   stream_.reset(new CodedOutputStream(&adapter_));
818   done_ = true;
819 }
820 
WriteTag(const google::protobuf::Field & field)821 void ProtoWriter::WriteTag(const google::protobuf::Field& field) {
822   WireFormatLite::WireType wire_type = WireFormatLite::WireTypeForFieldType(
823       static_cast<WireFormatLite::FieldType>(field.kind()));
824   stream_->WriteTag(WireFormatLite::MakeTag(field.number(), wire_type));
825 }
826 
827 
828 }  // namespace converter
829 }  // namespace util
830 }  // namespace protobuf
831 }  // namespace google
832