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