diff options
Diffstat (limited to 'src/google/protobuf/util/internal/proto_writer.cc')
-rw-r--r-- | src/google/protobuf/util/internal/proto_writer.cc | 259 |
1 files changed, 158 insertions, 101 deletions
diff --git a/src/google/protobuf/util/internal/proto_writer.cc b/src/google/protobuf/util/internal/proto_writer.cc index 47e0009e..b7a52db6 100644 --- a/src/google/protobuf/util/internal/proto_writer.cc +++ b/src/google/protobuf/util/internal/proto_writer.cc @@ -64,7 +64,9 @@ ProtoWriter::ProtoWriter(TypeResolver* type_resolver, typeinfo_(TypeInfo::NewTypeInfo(type_resolver)), own_typeinfo_(true), done_(false), - element_(NULL), + ignore_unknown_fields_(false), + use_lower_camel_for_enums_(false), + element_(nullptr), size_insert_(), output_(output), buffer_(), @@ -81,7 +83,9 @@ ProtoWriter::ProtoWriter(const TypeInfo* typeinfo, typeinfo_(typeinfo), own_typeinfo_(false), done_(false), - element_(NULL), + ignore_unknown_fields_(false), + use_lower_camel_for_enums_(false), + element_(nullptr), size_insert_(), output_(output), buffer_(), @@ -95,14 +99,14 @@ ProtoWriter::~ProtoWriter() { if (own_typeinfo_) { delete typeinfo_; } - if (element_ == NULL) return; + if (element_ == nullptr) return; // Cleanup explicitly in order to avoid destructor stack overflow when input // is deeply nested. // Cast to BaseElement to avoid doing additional checks (like missing fields) // during pop(). - google::protobuf::scoped_ptr<BaseElement> element( + std::unique_ptr<BaseElement> element( static_cast<BaseElement*>(element_.get())->pop<BaseElement>()); - while (element != NULL) { + while (element != nullptr) { element.reset(element->pop<BaseElement>()); } } @@ -262,8 +266,10 @@ inline Status WriteString(int field_number, const DataPiece& data, // Writes an ENUM field, including tag, to the stream. inline Status WriteEnum(int field_number, const DataPiece& data, const google::protobuf::Enum* enum_type, - CodedOutputStream* stream) { - StatusOr<int> e = data.ToEnum(enum_type); + CodedOutputStream* stream, + bool use_lower_camel_for_enums, + bool ignore_unknown_values) { + StatusOr<int> e = data.ToEnum(enum_type, use_lower_camel_for_enums, ignore_unknown_values); if (e.ok()) { WireFormatLite::WriteEnum(field_number, e.ValueOrDie(), stream); } @@ -289,14 +295,20 @@ std::set<const google::protobuf::Field*> GetRequiredFields( ProtoWriter::ProtoElement::ProtoElement(const TypeInfo* typeinfo, const google::protobuf::Type& type, ProtoWriter* enclosing) - : BaseElement(NULL), + : BaseElement(nullptr), ow_(enclosing), - parent_field_(NULL), + parent_field_(nullptr), typeinfo_(typeinfo), + proto3_(type.syntax() == google::protobuf::SYNTAX_PROTO3), type_(type), - required_fields_(GetRequiredFields(type)), size_index_(-1), - array_index_(-1) {} + array_index_(-1), + // oneof_indices_ values are 1-indexed (0 means not present). + oneof_indices_(type.oneofs_size() + 1) { + if (!proto3_) { + required_fields_ = GetRequiredFields(type_); + } +} ProtoWriter::ProtoElement::ProtoElement(ProtoWriter::ProtoElement* parent, const google::protobuf::Field* field, @@ -306,22 +318,28 @@ ProtoWriter::ProtoElement::ProtoElement(ProtoWriter::ProtoElement* parent, ow_(this->parent()->ow_), parent_field_(field), typeinfo_(this->parent()->typeinfo_), + proto3_(type.syntax() == google::protobuf::SYNTAX_PROTO3), type_(type), size_index_( !is_list && field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE ? ow_->size_insert_.size() : -1), - array_index_(is_list ? 0 : -1) { + array_index_(is_list ? 0 : -1), + // oneof_indices_ values are 1-indexed (0 means not present). + oneof_indices_(type_.oneofs_size() + 1) { if (!is_list) { if (ow_->IsRepeated(*field)) { // Update array_index_ if it is an explicit list. if (this->parent()->array_index_ >= 0) this->parent()->array_index_++; - } else { + } else if (!proto3_) { + // For required fields tracking. this->parent()->RegisterField(field); } if (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) { - required_fields_ = GetRequiredFields(type_); + if (!proto3_) { + required_fields_ = GetRequiredFields(type_); + } int start_pos = ow_->stream_->ByteCount(); // length of serialized message is the final buffer position minus // starting buffer position, plus length adjustments for size fields @@ -334,12 +352,14 @@ ProtoWriter::ProtoElement::ProtoElement(ProtoWriter::ProtoElement* parent, } ProtoWriter::ProtoElement* ProtoWriter::ProtoElement::pop() { - // Calls the registered error listener for any required field(s) not yet - // seen. - for (set<const google::protobuf::Field*>::iterator it = - required_fields_.begin(); - it != required_fields_.end(); ++it) { - ow_->MissingField((*it)->name()); + if (!proto3_) { + // Calls the registered error listener for any required field(s) not yet + // seen. + for (std::set<const google::protobuf::Field*>::iterator it = + required_fields_.begin(); + it != required_fields_.end(); ++it) { + ow_->MissingField((*it)->name()); + } } // Computes the total number of proto bytes used by a message, also adjusts // the size of all parent messages by the length of this size field. @@ -355,7 +375,7 @@ ProtoWriter::ProtoElement* ProtoWriter::ProtoElement::pop() { // all enclosing messages. int size = ow_->size_insert_[size_index_].size; int length = CodedOutputStream::VarintSize32(size); - for (ProtoElement* e = parent(); e != NULL; e = e->parent()) { + for (ProtoElement* e = parent(); e != nullptr; e = e->parent()) { // Only nested messages have size field, lists do not have size field. if (e->size_index_ >= 0) { ow_->size_insert_[e->size_index_].size += length; @@ -375,7 +395,7 @@ void ProtoWriter::ProtoElement::RegisterField( } string ProtoWriter::ProtoElement::ToString() const { - if (parent() == NULL) return ""; + if (parent() == nullptr) return ""; string loc = parent()->ToString(); if (!ow_->IsRepeated(*parent_field_) || parent()->parent_field_ != parent_field_) { @@ -399,11 +419,11 @@ string ProtoWriter::ProtoElement::ToString() const { } bool ProtoWriter::ProtoElement::IsOneofIndexTaken(int32 index) { - return ContainsKey(oneof_indices_, index); + return oneof_indices_[index]; } void ProtoWriter::ProtoElement::TakeOneofIndex(int32 index) { - InsertIfNotPresent(&oneof_indices_, index); + oneof_indices_[index] = true; } void ProtoWriter::InvalidName(StringPiece unknown_name, StringPiece message) { @@ -420,7 +440,7 @@ void ProtoWriter::MissingField(StringPiece missing_name) { ProtoWriter* ProtoWriter::StartObject(StringPiece name) { // Starting the root message. Create the root ProtoElement and return. - if (element_ == NULL) { + if (element_ == nullptr) { if (!name.empty()) { InvalidName(name, "Root element should not be named."); } @@ -428,9 +448,9 @@ ProtoWriter* ProtoWriter::StartObject(StringPiece name) { return this; } - const google::protobuf::Field* field = NULL; + const google::protobuf::Field* field = nullptr; field = BeginNamed(name, false); - if (field == NULL) return this; + if (field == nullptr) return this; // Check to see if this field is a oneof and that no oneof in that group has // already been set. @@ -440,16 +460,14 @@ ProtoWriter* ProtoWriter::StartObject(StringPiece name) { } const google::protobuf::Type* type = LookupType(field); - if (type == NULL) { + if (type == nullptr) { ++invalid_depth_; InvalidName(name, StrCat("Missing descriptor for field: ", field->type_url())); return this; } - WriteTag(*field); - element_.reset(new ProtoElement(element_.release(), field, *type, false)); - return this; + return StartObjectField(*field, *type); } ProtoWriter* ProtoWriter::EndObject() { @@ -458,14 +476,14 @@ ProtoWriter* ProtoWriter::EndObject() { return this; } - if (element_ != NULL) { + if (element_ != nullptr) { element_.reset(element_->pop()); } // If ending the root element, // then serialize the full message with calculated sizes. - if (element_ == NULL) { + if (element_ == nullptr) { WriteRootMessage(); } return this; @@ -473,7 +491,7 @@ ProtoWriter* ProtoWriter::EndObject() { ProtoWriter* ProtoWriter::StartList(StringPiece name) { const google::protobuf::Field* field = BeginNamed(name, true); - if (field == NULL) return this; + if (field == nullptr) return this; if (!ValidOneof(*field, name)) { ++invalid_depth_; @@ -481,21 +499,20 @@ ProtoWriter* ProtoWriter::StartList(StringPiece name) { } const google::protobuf::Type* type = LookupType(field); - if (type == NULL) { + if (type == nullptr) { ++invalid_depth_; InvalidName(name, StrCat("Missing descriptor for field: ", field->type_url())); return this; } - element_.reset(new ProtoElement(element_.release(), field, *type, true)); - return this; + return StartListField(*field, *type); } ProtoWriter* ProtoWriter::EndList() { if (invalid_depth_ > 0) { --invalid_depth_; - } else if (element_ != NULL) { + } else if (element_ != nullptr) { element_.reset(element_->pop()); } return this; @@ -507,96 +524,150 @@ ProtoWriter* ProtoWriter::RenderDataPiece(StringPiece name, if (invalid_depth_ > 0) return this; const google::protobuf::Field* field = Lookup(name); - if (field == NULL) return this; + if (field == nullptr) return this; if (!ValidOneof(*field, name)) return this; const google::protobuf::Type* type = LookupType(field); - if (type == NULL) { + if (type == nullptr) { InvalidName(name, StrCat("Missing descriptor for field: ", field->type_url())); return this; } + return RenderPrimitiveField(*field, *type, data); +} + +bool ProtoWriter::ValidOneof(const google::protobuf::Field& field, + StringPiece unnormalized_name) { + if (element_ == nullptr) return true; + + if (field.oneof_index() > 0) { + if (element_->IsOneofIndexTaken(field.oneof_index())) { + InvalidValue( + "oneof", + StrCat("oneof field '", + element_->type().oneofs(field.oneof_index() - 1), + "' is already set. Cannot set '", unnormalized_name, "'")); + return false; + } + element_->TakeOneofIndex(field.oneof_index()); + } + return true; +} + +bool ProtoWriter::IsRepeated(const google::protobuf::Field& field) { + return field.cardinality() == + google::protobuf::Field_Cardinality_CARDINALITY_REPEATED; +} + +ProtoWriter* ProtoWriter::StartObjectField(const google::protobuf::Field& field, + const google::protobuf::Type& type) { + WriteTag(field); + element_.reset(new ProtoElement(element_.release(), &field, type, false)); + return this; +} + +ProtoWriter* ProtoWriter::StartListField(const google::protobuf::Field& field, + const google::protobuf::Type& type) { + element_.reset(new ProtoElement(element_.release(), &field, type, true)); + return this; +} + +ProtoWriter* ProtoWriter::RenderPrimitiveField( + const google::protobuf::Field& field, const google::protobuf::Type& type, + const DataPiece& data) { + Status status; + // Pushing a ProtoElement and then pop it off at the end for 2 purposes: // error location reporting and required field accounting. - element_.reset(new ProtoElement(element_.release(), field, *type, false)); - - if (field->kind() == google::protobuf::Field_Kind_TYPE_UNKNOWN || - field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) { - InvalidValue(field->type_url().empty() - ? google::protobuf::Field_Kind_Name(field->kind()) - : field->type_url(), + // + // For proto3, since there is no required field tracking, we only need to push + // ProtoElement for error cases. + if (!element_->proto3()) { + element_.reset(new ProtoElement(element_.release(), &field, type, false)); + } + + if (field.kind() == google::protobuf::Field_Kind_TYPE_UNKNOWN || + field.kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) { + // Push a ProtoElement for location reporting purposes. + if (element_->proto3()) { + element_.reset(new ProtoElement(element_.release(), &field, type, false)); + } + InvalidValue(field.type_url().empty() + ? google::protobuf::Field_Kind_Name(field.kind()) + : field.type_url(), data.ValueAsStringOrDefault("")); element_.reset(element()->pop()); return this; } - switch (field->kind()) { + switch (field.kind()) { case google::protobuf::Field_Kind_TYPE_INT32: { - status = WriteInt32(field->number(), data, stream_.get()); + status = WriteInt32(field.number(), data, stream_.get()); break; } case google::protobuf::Field_Kind_TYPE_SFIXED32: { - status = WriteSFixed32(field->number(), data, stream_.get()); + status = WriteSFixed32(field.number(), data, stream_.get()); break; } case google::protobuf::Field_Kind_TYPE_SINT32: { - status = WriteSInt32(field->number(), data, stream_.get()); + status = WriteSInt32(field.number(), data, stream_.get()); break; } case google::protobuf::Field_Kind_TYPE_FIXED32: { - status = WriteFixed32(field->number(), data, stream_.get()); + status = WriteFixed32(field.number(), data, stream_.get()); break; } case google::protobuf::Field_Kind_TYPE_UINT32: { - status = WriteUInt32(field->number(), data, stream_.get()); + status = WriteUInt32(field.number(), data, stream_.get()); break; } case google::protobuf::Field_Kind_TYPE_INT64: { - status = WriteInt64(field->number(), data, stream_.get()); + status = WriteInt64(field.number(), data, stream_.get()); break; } case google::protobuf::Field_Kind_TYPE_SFIXED64: { - status = WriteSFixed64(field->number(), data, stream_.get()); + status = WriteSFixed64(field.number(), data, stream_.get()); break; } case google::protobuf::Field_Kind_TYPE_SINT64: { - status = WriteSInt64(field->number(), data, stream_.get()); + status = WriteSInt64(field.number(), data, stream_.get()); break; } case google::protobuf::Field_Kind_TYPE_FIXED64: { - status = WriteFixed64(field->number(), data, stream_.get()); + status = WriteFixed64(field.number(), data, stream_.get()); break; } case google::protobuf::Field_Kind_TYPE_UINT64: { - status = WriteUInt64(field->number(), data, stream_.get()); + status = WriteUInt64(field.number(), data, stream_.get()); break; } case google::protobuf::Field_Kind_TYPE_DOUBLE: { - status = WriteDouble(field->number(), data, stream_.get()); + status = WriteDouble(field.number(), data, stream_.get()); break; } case google::protobuf::Field_Kind_TYPE_FLOAT: { - status = WriteFloat(field->number(), data, stream_.get()); + status = WriteFloat(field.number(), data, stream_.get()); break; } case google::protobuf::Field_Kind_TYPE_BOOL: { - status = WriteBool(field->number(), data, stream_.get()); + status = WriteBool(field.number(), data, stream_.get()); break; } case google::protobuf::Field_Kind_TYPE_BYTES: { - status = WriteBytes(field->number(), data, stream_.get()); + status = WriteBytes(field.number(), data, stream_.get()); break; } case google::protobuf::Field_Kind_TYPE_STRING: { - status = WriteString(field->number(), data, stream_.get()); + status = WriteString(field.number(), data, stream_.get()); break; } case google::protobuf::Field_Kind_TYPE_ENUM: { - status = WriteEnum(field->number(), data, - typeinfo_->GetEnumByTypeUrl(field->type_url()), - stream_.get()); + status = WriteEnum(field.number(), data, + typeinfo_->GetEnumByTypeUrl(field.type_url()), + stream_.get(), use_lower_camel_for_enums_, + ignore_unknown_fields_); break; } default: // TYPE_GROUP or TYPE_MESSAGE @@ -604,53 +675,37 @@ ProtoWriter* ProtoWriter::RenderDataPiece(StringPiece name, } if (!status.ok()) { - InvalidValue(google::protobuf::Field_Kind_Name(field->kind()), + // Push a ProtoElement for location reporting purposes. + if (element_->proto3()) { + element_.reset(new ProtoElement(element_.release(), &field, type, false)); + } + InvalidValue(google::protobuf::Field_Kind_Name(field.kind()), status.error_message()); + element_.reset(element()->pop()); + return this; } - element_.reset(element()->pop()); - return this; -} - -bool ProtoWriter::ValidOneof(const google::protobuf::Field& field, - StringPiece unnormalized_name) { - if (element_ == NULL) return true; - - if (field.oneof_index() > 0) { - if (element_->IsOneofIndexTaken(field.oneof_index())) { - InvalidValue( - "oneof", - StrCat("oneof field '", - element_->type().oneofs(field.oneof_index() - 1), - "' is already set. Cannot set '", unnormalized_name, "'")); - return false; - } - element_->TakeOneofIndex(field.oneof_index()); - } - return true; -} + if (!element_->proto3()) element_.reset(element()->pop()); -bool ProtoWriter::IsRepeated(const google::protobuf::Field& field) { - return field.cardinality() == - google::protobuf::Field_Cardinality_CARDINALITY_REPEATED; + return this; } const google::protobuf::Field* ProtoWriter::BeginNamed(StringPiece name, bool is_list) { if (invalid_depth_ > 0) { ++invalid_depth_; - return NULL; + return nullptr; } const google::protobuf::Field* field = Lookup(name); - if (field == NULL) { + if (field == nullptr) { ++invalid_depth_; // InvalidName() already called in Lookup(). - return NULL; + return nullptr; } if (is_list && !IsRepeated(*field)) { ++invalid_depth_; InvalidName(name, "Proto field is not repeating, cannot start list."); - return NULL; + return nullptr; } return field; } @@ -658,23 +713,25 @@ const google::protobuf::Field* ProtoWriter::BeginNamed(StringPiece name, const google::protobuf::Field* ProtoWriter::Lookup( StringPiece unnormalized_name) { ProtoElement* e = element(); - if (e == NULL) { + if (e == nullptr) { InvalidName(unnormalized_name, "Root element must be a message."); - return NULL; + return nullptr; } if (unnormalized_name.empty()) { // Objects in repeated field inherit the same field descriptor. - if (e->parent_field() == NULL) { + if (e->parent_field() == nullptr) { InvalidName(unnormalized_name, "Proto fields must have a name."); } else if (!IsRepeated(*e->parent_field())) { InvalidName(unnormalized_name, "Proto fields must have a name."); - return NULL; + return nullptr; } return e->parent_field(); } const google::protobuf::Field* field = typeinfo_->FindField(&e->type(), unnormalized_name); - if (field == NULL) InvalidName(unnormalized_name, "Cannot find field."); + if (field == nullptr && !ignore_unknown_fields_) { + InvalidName(unnormalized_name, "Cannot find field."); + } return field; } @@ -691,7 +748,7 @@ void ProtoWriter::WriteRootMessage() { int curr_pos = 0; // Calls the destructor of CodedOutputStream to remove any uninitialized // memory from the Cord before we read it. - stream_.reset(NULL); + stream_.reset(nullptr); const void* data; int length; google::protobuf::io::ArrayInputStream input_stream(buffer_.data(), buffer_.size()); |