aboutsummaryrefslogtreecommitdiff
path: root/src/google/protobuf/util/internal/proto_writer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/google/protobuf/util/internal/proto_writer.cc')
-rw-r--r--src/google/protobuf/util/internal/proto_writer.cc259
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());