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/wire_format_lite.h>
38 #include <google/protobuf/util/internal/field_mask_utility.h>
39 #include <google/protobuf/util/internal/object_location_tracker.h>
40 #include <google/protobuf/util/internal/constants.h>
41 #include <google/protobuf/util/internal/utility.h>
42 #include <google/protobuf/stubs/strutil.h>
43 #include <google/protobuf/stubs/time.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.value(), 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.value(), 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.value(), 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.value(), 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.value(), 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.value(), 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.value(), 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.value(), 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.value(), 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.value(), 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.value(), 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.value(), 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.value(), 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.value(), 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() == google::protobuf::Field::CARDINALITY_REQUIRED) {
279 required.insert(&field);
280 }
281 }
282 return required;
283 }
284
285 } // namespace
286
ProtoElement(const TypeInfo * typeinfo,const google::protobuf::Type & type,ProtoWriter * enclosing)287 ProtoWriter::ProtoElement::ProtoElement(const TypeInfo* typeinfo,
288 const google::protobuf::Type& type,
289 ProtoWriter* enclosing)
290 : BaseElement(nullptr),
291 ow_(enclosing),
292 parent_field_(nullptr),
293 typeinfo_(typeinfo),
294 proto3_(type.syntax() == google::protobuf::SYNTAX_PROTO3),
295 type_(type),
296 size_index_(-1),
297 array_index_(-1),
298 // oneof_indices_ values are 1-indexed (0 means not present).
299 oneof_indices_(type.oneofs_size() + 1) {
300 if (!proto3_) {
301 required_fields_ = GetRequiredFields(type_);
302 }
303 }
304
ProtoElement(ProtoWriter::ProtoElement * parent,const google::protobuf::Field * field,const google::protobuf::Type & type,bool is_list)305 ProtoWriter::ProtoElement::ProtoElement(ProtoWriter::ProtoElement* parent,
306 const google::protobuf::Field* field,
307 const google::protobuf::Type& type,
308 bool is_list)
309 : BaseElement(parent),
310 ow_(this->parent()->ow_),
311 parent_field_(field),
312 typeinfo_(this->parent()->typeinfo_),
313 proto3_(type.syntax() == google::protobuf::SYNTAX_PROTO3),
314 type_(type),
315 size_index_(!is_list &&
316 field->kind() == google::protobuf::Field::TYPE_MESSAGE
317 ? ow_->size_insert_.size()
318 : -1),
319 array_index_(is_list ? 0 : -1),
320 // oneof_indices_ values are 1-indexed (0 means not present).
321 oneof_indices_(type_.oneofs_size() + 1) {
322 if (!is_list) {
323 if (ow_->IsRepeated(*field)) {
324 // Update array_index_ if it is an explicit list.
325 if (this->parent()->array_index_ >= 0) this->parent()->array_index_++;
326 } else if (!proto3_) {
327 // For required fields tracking.
328 this->parent()->RegisterField(field);
329 }
330
331 if (field->kind() == google::protobuf::Field::TYPE_MESSAGE) {
332 if (!proto3_) {
333 required_fields_ = GetRequiredFields(type_);
334 }
335 int start_pos = ow_->stream_->ByteCount();
336 // length of serialized message is the final buffer position minus
337 // starting buffer position, plus length adjustments for size fields
338 // of any nested messages. We start with -start_pos here, so we only
339 // need to add the final buffer position to it at the end.
340 SizeInfo info = {start_pos, -start_pos};
341 ow_->size_insert_.push_back(info);
342 }
343 }
344 }
345
pop()346 ProtoWriter::ProtoElement* ProtoWriter::ProtoElement::pop() {
347 if (!proto3_) {
348 // Calls the registered error listener for any required field(s) not yet
349 // seen.
350 for (std::set<const google::protobuf::Field*>::iterator it =
351 required_fields_.begin();
352 it != required_fields_.end(); ++it) {
353 ow_->MissingField((*it)->name());
354 }
355 }
356 // Computes the total number of proto bytes used by a message, also adjusts
357 // the size of all parent messages by the length of this size field.
358 // If size_index_ < 0, this is not a message, so no size field is added.
359 if (size_index_ >= 0) {
360 // Add the final buffer position to compute the total length of this
361 // serialized message. The stored value (before this addition) already
362 // contains the total length of the size fields of all nested messages
363 // minus the initial buffer position.
364 ow_->size_insert_[size_index_].size += ow_->stream_->ByteCount();
365 // Calculate the length required to serialize the size field of the
366 // message, and propagate this additional size information upward to
367 // all enclosing messages.
368 int size = ow_->size_insert_[size_index_].size;
369 int length = CodedOutputStream::VarintSize32(size);
370 for (ProtoElement* e = parent(); e != nullptr; e = e->parent()) {
371 // Only nested messages have size field, lists do not have size field.
372 if (e->size_index_ >= 0) {
373 ow_->size_insert_[e->size_index_].size += length;
374 }
375 }
376 }
377 return BaseElement::pop<ProtoElement>();
378 }
379
RegisterField(const google::protobuf::Field * field)380 void ProtoWriter::ProtoElement::RegisterField(
381 const google::protobuf::Field* field) {
382 if (!required_fields_.empty() &&
383 field->cardinality() == google::protobuf::Field::CARDINALITY_REQUIRED) {
384 required_fields_.erase(field);
385 }
386 }
387
ToString() const388 std::string ProtoWriter::ProtoElement::ToString() const {
389 std::string loc = "";
390
391 // first populate a stack with the nodes since we need to process them
392 // from root to leaf when generating the string location
393 const ProtoWriter::ProtoElement* now = this;
394 std::stack<const ProtoWriter::ProtoElement*> element_stack;
395 while (now->parent() != nullptr) {
396 element_stack.push(now);
397 now = now->parent();
398 }
399
400 // now pop each node from the stack and append to the location string
401 while (!element_stack.empty()) {
402 now = element_stack.top();
403 element_stack.pop();
404
405 if (!ow_->IsRepeated(*(now->parent_field_)) ||
406 now->parent()->parent_field_ != now->parent_field_) {
407 std::string name = now->parent_field_->name();
408 int i = 0;
409 while (i < name.size() &&
410 (ascii_isalnum(name[i]) || name[i] == '_'))
411 ++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(), 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(
455 StringPiece name) {
456 // Starting the root message. Create the root ProtoElement and return.
457 if (element_ == nullptr) {
458 if (!name.empty()) {
459 InvalidName(name, "Root element should not be named.");
460 }
461 element_.reset(new ProtoElement(typeinfo_, master_type_, this));
462 return this;
463 }
464
465 const google::protobuf::Field* field = BeginNamed(name, false);
466
467 if (field == nullptr) return this;
468
469 // Check to see if this field is a oneof and that no oneof in that group has
470 // already been set.
471 if (!ValidOneof(*field, name)) {
472 ++invalid_depth_;
473 return this;
474 }
475
476 const google::protobuf::Type* type = LookupType(field);
477 if (type == nullptr) {
478 ++invalid_depth_;
479 InvalidName(name, StrCat("Missing descriptor for field: ",
480 field->type_url()));
481 return this;
482 }
483
484 return StartObjectField(*field, *type);
485 }
486
487
EndObject()488 ProtoWriter* ProtoWriter::EndObject() {
489 if (invalid_depth_ > 0) {
490 --invalid_depth_;
491 return this;
492 }
493
494 if (element_ != nullptr) {
495 element_.reset(element_->pop());
496 }
497
498
499 // If ending the root element,
500 // then serialize the full message with calculated sizes.
501 if (element_ == nullptr) {
502 WriteRootMessage();
503 }
504 return this;
505 }
506
StartList(StringPiece name)507 ProtoWriter* ProtoWriter::StartList(
508 StringPiece name) {
509
510 const google::protobuf::Field* field = BeginNamed(name, true);
511
512 if (field == nullptr) return this;
513
514 if (!ValidOneof(*field, name)) {
515 ++invalid_depth_;
516 return this;
517 }
518
519 const google::protobuf::Type* type = LookupType(field);
520 if (type == nullptr) {
521 ++invalid_depth_;
522 InvalidName(name, StrCat("Missing descriptor for field: ",
523 field->type_url()));
524 return this;
525 }
526
527 return StartListField(*field, *type);
528 }
529
530
EndList()531 ProtoWriter* ProtoWriter::EndList() {
532 if (invalid_depth_ > 0) {
533 --invalid_depth_;
534 } else if (element_ != nullptr) {
535 element_.reset(element_->pop());
536 }
537 return this;
538 }
539
RenderDataPiece(StringPiece name,const DataPiece & data)540 ProtoWriter* ProtoWriter::RenderDataPiece(
541 StringPiece name, const DataPiece& data) {
542 Status status;
543 if (invalid_depth_ > 0) return this;
544
545 const google::protobuf::Field* field = Lookup(name);
546
547 if (field == nullptr) return this;
548
549 if (!ValidOneof(*field, name)) return this;
550
551 const google::protobuf::Type* type = LookupType(field);
552 if (type == nullptr) {
553 InvalidName(name, StrCat("Missing descriptor for field: ",
554 field->type_url()));
555 return this;
556 }
557
558 return RenderPrimitiveField(*field, *type, data);
559 }
560
ValidOneof(const google::protobuf::Field & field,StringPiece unnormalized_name)561 bool ProtoWriter::ValidOneof(const google::protobuf::Field& field,
562 StringPiece unnormalized_name) {
563 if (element_ == nullptr) return true;
564
565 if (field.oneof_index() > 0) {
566 if (element_->IsOneofIndexTaken(field.oneof_index())) {
567 InvalidValue(
568 "oneof",
569 StrCat(
570 "oneof field '", element_->type().oneofs(field.oneof_index() - 1),
571 "' is already set. Cannot set '", unnormalized_name, "'"));
572 return false;
573 }
574 element_->TakeOneofIndex(field.oneof_index());
575 }
576 return true;
577 }
578
IsRepeated(const google::protobuf::Field & field)579 bool ProtoWriter::IsRepeated(const google::protobuf::Field& field) {
580 return field.cardinality() == google::protobuf::Field::CARDINALITY_REPEATED;
581 }
582
StartObjectField(const google::protobuf::Field & field,const google::protobuf::Type & type)583 ProtoWriter* ProtoWriter::StartObjectField(const google::protobuf::Field& field,
584 const google::protobuf::Type& type) {
585 WriteTag(field);
586 element_.reset(new ProtoElement(element_.release(), &field, type, false));
587 return this;
588 }
589
StartListField(const google::protobuf::Field & field,const google::protobuf::Type & type)590 ProtoWriter* ProtoWriter::StartListField(const google::protobuf::Field& field,
591 const google::protobuf::Type& type) {
592 element_.reset(new ProtoElement(element_.release(), &field, type, true));
593 return this;
594 }
595
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)596 Status ProtoWriter::WriteEnum(int field_number, const DataPiece& data,
597 const google::protobuf::Enum* enum_type,
598 CodedOutputStream* stream,
599 bool use_lower_camel_for_enums,
600 bool case_insensitive_enum_parsing,
601 bool ignore_unknown_values) {
602 bool is_unknown_enum_value = false;
603 StatusOr<int> e = data.ToEnum(enum_type, use_lower_camel_for_enums,
604 case_insensitive_enum_parsing,
605 ignore_unknown_values, &is_unknown_enum_value);
606 if (e.ok() && !is_unknown_enum_value) {
607 WireFormatLite::WriteEnum(field_number, e.value(), stream);
608 }
609 return e.status();
610 }
611
RenderPrimitiveField(const google::protobuf::Field & field,const google::protobuf::Type & type,const DataPiece & data)612 ProtoWriter* ProtoWriter::RenderPrimitiveField(
613 const google::protobuf::Field& field, const google::protobuf::Type& type,
614 const DataPiece& data) {
615 Status status;
616
617 // Pushing a ProtoElement and then pop it off at the end for 2 purposes:
618 // error location reporting and required field accounting.
619 //
620 // For proto3, since there is no required field tracking, we only need to push
621 // ProtoElement for error cases.
622 if (!element_->proto3()) {
623 element_.reset(new ProtoElement(element_.release(), &field, type, false));
624 }
625
626 if (field.kind() == google::protobuf::Field::TYPE_UNKNOWN ||
627 field.kind() == google::protobuf::Field::TYPE_MESSAGE) {
628 // Push a ProtoElement for location reporting purposes.
629 if (element_->proto3()) {
630 element_.reset(new ProtoElement(element_.release(), &field, type, false));
631 }
632 InvalidValue(field.type_url().empty()
633 ? google::protobuf::Field_Kind_Name(field.kind())
634 : field.type_url(),
635 data.ValueAsStringOrDefault(""));
636 element_.reset(element()->pop());
637 return this;
638 }
639
640 switch (field.kind()) {
641 case google::protobuf::Field::TYPE_INT32: {
642 status = WriteInt32(field.number(), data, stream_.get());
643 break;
644 }
645 case google::protobuf::Field::TYPE_SFIXED32: {
646 status = WriteSFixed32(field.number(), data, stream_.get());
647 break;
648 }
649 case google::protobuf::Field::TYPE_SINT32: {
650 status = WriteSInt32(field.number(), data, stream_.get());
651 break;
652 }
653 case google::protobuf::Field::TYPE_FIXED32: {
654 status = WriteFixed32(field.number(), data, stream_.get());
655 break;
656 }
657 case google::protobuf::Field::TYPE_UINT32: {
658 status = WriteUInt32(field.number(), data, stream_.get());
659 break;
660 }
661 case google::protobuf::Field::TYPE_INT64: {
662 status = WriteInt64(field.number(), data, stream_.get());
663 break;
664 }
665 case google::protobuf::Field::TYPE_SFIXED64: {
666 status = WriteSFixed64(field.number(), data, stream_.get());
667 break;
668 }
669 case google::protobuf::Field::TYPE_SINT64: {
670 status = WriteSInt64(field.number(), data, stream_.get());
671 break;
672 }
673 case google::protobuf::Field::TYPE_FIXED64: {
674 status = WriteFixed64(field.number(), data, stream_.get());
675 break;
676 }
677 case google::protobuf::Field::TYPE_UINT64: {
678 status = WriteUInt64(field.number(), data, stream_.get());
679 break;
680 }
681 case google::protobuf::Field::TYPE_DOUBLE: {
682 status = WriteDouble(field.number(), data, stream_.get());
683 break;
684 }
685 case google::protobuf::Field::TYPE_FLOAT: {
686 status = WriteFloat(field.number(), data, stream_.get());
687 break;
688 }
689 case google::protobuf::Field::TYPE_BOOL: {
690 status = WriteBool(field.number(), data, stream_.get());
691 break;
692 }
693 case google::protobuf::Field::TYPE_BYTES: {
694 status = WriteBytes(field.number(), data, stream_.get());
695 break;
696 }
697 case google::protobuf::Field::TYPE_STRING: {
698 status = WriteString(field.number(), data, stream_.get());
699 break;
700 }
701 case google::protobuf::Field::TYPE_ENUM: {
702 status = WriteEnum(
703 field.number(), data, typeinfo_->GetEnumByTypeUrl(field.type_url()),
704 stream_.get(), use_lower_camel_for_enums_,
705 case_insensitive_enum_parsing_, ignore_unknown_enum_values_);
706 break;
707 }
708 default: // TYPE_GROUP or TYPE_MESSAGE
709 status =
710 Status(util::error::INVALID_ARGUMENT, data.ToString().value());
711 }
712
713 if (!status.ok()) {
714 // Push a ProtoElement for location reporting purposes.
715 if (element_->proto3()) {
716 element_.reset(new ProtoElement(element_.release(), &field, type, false));
717 }
718 InvalidValue(google::protobuf::Field_Kind_Name(field.kind()),
719 status.message());
720 element_.reset(element()->pop());
721 return this;
722 }
723
724 if (!element_->proto3()) element_.reset(element()->pop());
725
726 return this;
727 }
728
BeginNamed(StringPiece name,bool is_list)729 const google::protobuf::Field* ProtoWriter::BeginNamed(StringPiece name,
730 bool is_list) {
731 if (invalid_depth_ > 0) {
732 ++invalid_depth_;
733 return nullptr;
734 }
735 const google::protobuf::Field* field = Lookup(name);
736 if (field == nullptr) {
737 ++invalid_depth_;
738 // InvalidName() already called in Lookup().
739 return nullptr;
740 }
741 if (is_list && !IsRepeated(*field)) {
742 ++invalid_depth_;
743 InvalidName(name, "Proto field is not repeating, cannot start list.");
744 return nullptr;
745 }
746 return field;
747 }
748
Lookup(StringPiece unnormalized_name)749 const google::protobuf::Field* ProtoWriter::Lookup(
750 StringPiece unnormalized_name) {
751 ProtoElement* e = element();
752 if (e == nullptr) {
753 InvalidName(unnormalized_name, "Root element must be a message.");
754 return nullptr;
755 }
756 if (unnormalized_name.empty()) {
757 // Objects in repeated field inherit the same field descriptor.
758 if (e->parent_field() == nullptr) {
759 InvalidName(unnormalized_name, "Proto fields must have a name.");
760 } else if (!IsRepeated(*e->parent_field())) {
761 InvalidName(unnormalized_name, "Proto fields must have a name.");
762 return nullptr;
763 }
764 return e->parent_field();
765 }
766 const google::protobuf::Field* field =
767 typeinfo_->FindField(&e->type(), unnormalized_name);
768 if (field == nullptr && !ignore_unknown_fields_) {
769 InvalidName(unnormalized_name, "Cannot find field.");
770 }
771 return field;
772 }
773
LookupType(const google::protobuf::Field * field)774 const google::protobuf::Type* ProtoWriter::LookupType(
775 const google::protobuf::Field* field) {
776 return ((field->kind() == google::protobuf::Field::TYPE_MESSAGE ||
777 field->kind() == google::protobuf::Field::TYPE_GROUP)
778 ? typeinfo_->GetTypeByTypeUrl(field->type_url())
779 : &element_->type());
780 }
781
WriteRootMessage()782 void ProtoWriter::WriteRootMessage() {
783 GOOGLE_DCHECK(!done_);
784 int curr_pos = 0;
785 // Calls the destructor of CodedOutputStream to remove any uninitialized
786 // memory from the Cord before we read it.
787 stream_.reset(nullptr);
788 const void* data;
789 int length;
790 io::ArrayInputStream input_stream(buffer_.data(), buffer_.size());
791 while (input_stream.Next(&data, &length)) {
792 if (length == 0) continue;
793 int num_bytes = length;
794 // Write up to where we need to insert the size field.
795 // The number of bytes we may write is the smaller of:
796 // - the current fragment size
797 // - the distance to the next position where a size field needs to be
798 // inserted.
799 if (!size_insert_.empty() &&
800 size_insert_.front().pos - curr_pos < num_bytes) {
801 num_bytes = size_insert_.front().pos - curr_pos;
802 }
803 output_->Append(static_cast<const char*>(data), num_bytes);
804 if (num_bytes < length) {
805 input_stream.BackUp(length - num_bytes);
806 }
807 curr_pos += num_bytes;
808 // Insert the size field.
809 // size_insert_.front(): the next <index, size> pair to be written.
810 // size_insert_.front().pos: position of the size field.
811 // size_insert_.front().size: the size (integer) to be inserted.
812 if (!size_insert_.empty() && curr_pos == size_insert_.front().pos) {
813 // Varint32 occupies at most 10 bytes.
814 uint8 insert_buffer[10];
815 uint8* insert_buffer_pos = CodedOutputStream::WriteVarint32ToArray(
816 size_insert_.front().size, insert_buffer);
817 output_->Append(reinterpret_cast<const char*>(insert_buffer),
818 insert_buffer_pos - insert_buffer);
819 size_insert_.pop_front();
820 }
821 }
822 output_->Flush();
823 stream_.reset(new CodedOutputStream(&adapter_));
824 done_ = true;
825 }
826
WriteTag(const google::protobuf::Field & field)827 void ProtoWriter::WriteTag(const google::protobuf::Field& field) {
828 WireFormatLite::WireType wire_type = WireFormatLite::WireTypeForFieldType(
829 static_cast<WireFormatLite::FieldType>(field.kind()));
830 stream_->WriteTag(WireFormatLite::MakeTag(field.number(), wire_type));
831 }
832
833
834 } // namespace converter
835 } // namespace util
836 } // namespace protobuf
837 } // namespace google
838