diff options
author | Adam Cozzette <acozzette@google.com> | 2016-06-29 15:23:27 -0700 |
---|---|---|
committer | Adam Cozzette <acozzette@google.com> | 2016-06-29 15:38:03 -0700 |
commit | d64a2d9941c36a7bc2a7959ea10ab8363192ac14 (patch) | |
tree | 52330d146ad63d3d70f3baade00d5d1fea8f5e0c /src/google/protobuf/util | |
parent | c18aa7795a2e02ef700ff8b039d94ecdcc33432f (diff) |
Integrated internal changes from Google
This includes all internal changes from around May 20 to now.
Diffstat (limited to 'src/google/protobuf/util')
18 files changed, 284 insertions, 39 deletions
diff --git a/src/google/protobuf/util/field_mask_util.cc b/src/google/protobuf/util/field_mask_util.cc index 547c9fb5..409010a0 100644 --- a/src/google/protobuf/util/field_mask_util.cc +++ b/src/google/protobuf/util/field_mask_util.cc @@ -200,6 +200,15 @@ class FieldMaskTree { MergeMessage(&root_, source, options, destination); } + // Trims all fields not specified by this tree from the given message. + void TrimMessage(Message* message) { + // Do nothing if the tree is empty. + if (root_.children.empty()) { + return; + } + TrimMessage(&root_, message); + } + private: struct Node { Node() {} @@ -233,6 +242,9 @@ class FieldMaskTree { const FieldMaskUtil::MergeOptions& options, Message* destination); + // Trims all fields not specified by this sub-tree from the given message. + void TrimMessage(const Node* node, Message* message); + Node root_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldMaskTree); @@ -367,11 +379,15 @@ void FieldMaskTree::MergeMessage(const Node* node, const Message& source, } if (!field->is_repeated()) { switch (field->cpp_type()) { -#define COPY_VALUE(TYPE, Name) \ - case FieldDescriptor::CPPTYPE_##TYPE: { \ - destination_reflection->Set##Name( \ - destination, field, source_reflection->Get##Name(source, field)); \ - break; \ +#define COPY_VALUE(TYPE, Name) \ + case FieldDescriptor::CPPTYPE_##TYPE: { \ + if (source_reflection->HasField(source, field)) { \ + destination_reflection->Set##Name( \ + destination, field, source_reflection->Get##Name(source, field)); \ + } else { \ + destination_reflection->ClearField(destination, field); \ + } \ + break; \ } COPY_VALUE(BOOL, Bool) COPY_VALUE(INT32, Int32) @@ -433,6 +449,26 @@ void FieldMaskTree::MergeMessage(const Node* node, const Message& source, } } +void FieldMaskTree::TrimMessage(const Node* node, Message* message) { + GOOGLE_DCHECK(!node->children.empty()); + const Reflection* reflection = message->GetReflection(); + const Descriptor* descriptor = message->GetDescriptor(); + const int32 field_count = descriptor->field_count(); + for (int index = 0; index < field_count; ++index) { + const FieldDescriptor* field = descriptor->field(index); + if (!ContainsKey(node->children, field->name())) { + reflection->ClearField(message, field); + } else { + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + Node* child = node->children.at(field->name()); + if (!child->children.empty()) { + TrimMessage(child, reflection->MutableMessage(message, field)); + } + } + } + } +} + } // namespace void FieldMaskUtil::ToCanonicalForm(const FieldMask& mask, FieldMask* out) { @@ -489,6 +525,14 @@ void FieldMaskUtil::MergeMessageTo(const Message& source, const FieldMask& mask, tree.MergeMessage(source, options, destination); } +void FieldMaskUtil::TrimMessage(const FieldMask& mask, Message* destination) { + // Build a FieldMaskTree and walk through the tree to merge all specified + // fields. + FieldMaskTree tree; + tree.MergeFromFieldMask(mask); + tree.TrimMessage(GOOGLE_CHECK_NOTNULL(destination)); +} + } // namespace util } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/util/field_mask_util.h b/src/google/protobuf/util/field_mask_util.h index 644161b9..e79b65e9 100644 --- a/src/google/protobuf/util/field_mask_util.h +++ b/src/google/protobuf/util/field_mask_util.h @@ -107,10 +107,16 @@ class LIBPROTOBUF_EXPORT FieldMaskUtil { static bool IsPathInFieldMask(StringPiece path, const FieldMask& mask); class MergeOptions; - // Merges fields specified in a FieldMask into another message. + // Merges fields specified in a FieldMask into another message. See the + // comments in MergeOptions regarding compatibility with + // google/protobuf/field_mask.proto static void MergeMessageTo(const Message& source, const FieldMask& mask, const MergeOptions& options, Message* destination); + // Removes from 'message' any field that is not represented in the given + // FieldMask. If the FieldMask is empty, does nothing. + static void TrimMessage(const FieldMask& mask, Message* message); + private: friend class SnakeCaseCamelCaseTest; // Converts a field name from snake_case to camelCase: @@ -148,6 +154,10 @@ class LIBPROTOBUF_EXPORT FieldMaskUtil { FieldMask* out); }; +// Note that for compatibility with the defined behaviour for FieldMask in +// google/protobuf/field_mask.proto, set replace_message_fields and +// replace_repeated_fields to 'true'. The default options are not compatible +// with google/protobuf/field_mask.proto. class LIBPROTOBUF_EXPORT FieldMaskUtil::MergeOptions { public: MergeOptions() diff --git a/src/google/protobuf/util/field_mask_util_test.cc b/src/google/protobuf/util/field_mask_util_test.cc index 9b7fb62a..43fb7905 100644 --- a/src/google/protobuf/util/field_mask_util_test.cc +++ b/src/google/protobuf/util/field_mask_util_test.cc @@ -349,6 +349,10 @@ TEST(FieldMaskUtilTest, MergeMessage) { dst.Clear(); \ FieldMaskUtil::MergeMessageTo(src, mask, options, &dst); \ EXPECT_EQ(tmp.DebugString(), dst.DebugString()); \ + src.clear_##field_name(); \ + tmp.clear_##field_name(); \ + FieldMaskUtil::MergeMessageTo(src, mask, options, &dst); \ + EXPECT_EQ(tmp.DebugString(), dst.DebugString()); \ } TEST_MERGE_ONE_PRIMITIVE_FIELD(optional_int32) TEST_MERGE_ONE_PRIMITIVE_FIELD(optional_int64) @@ -484,6 +488,117 @@ TEST(FieldMaskUtilTest, MergeMessage) { EXPECT_EQ(1234, nested_dst.payload().repeated_int32(0)); } +TEST(FieldMaskUtilTest, TrimMessage) { +#define TEST_TRIM_ONE_PRIMITIVE_FIELD(field_name) \ + { \ + TestAllTypes msg; \ + TestUtil::SetAllFields(&msg); \ + TestAllTypes tmp; \ + tmp.set_##field_name(msg.field_name()); \ + FieldMask mask; \ + mask.add_paths(#field_name); \ + FieldMaskUtil::TrimMessage(mask, &msg); \ + EXPECT_EQ(tmp.DebugString(), msg.DebugString()); \ + } + TEST_TRIM_ONE_PRIMITIVE_FIELD(optional_int32) + TEST_TRIM_ONE_PRIMITIVE_FIELD(optional_int64) + TEST_TRIM_ONE_PRIMITIVE_FIELD(optional_uint32) + TEST_TRIM_ONE_PRIMITIVE_FIELD(optional_uint64) + TEST_TRIM_ONE_PRIMITIVE_FIELD(optional_sint32) + TEST_TRIM_ONE_PRIMITIVE_FIELD(optional_sint64) + TEST_TRIM_ONE_PRIMITIVE_FIELD(optional_fixed32) + TEST_TRIM_ONE_PRIMITIVE_FIELD(optional_fixed64) + TEST_TRIM_ONE_PRIMITIVE_FIELD(optional_sfixed32) + TEST_TRIM_ONE_PRIMITIVE_FIELD(optional_sfixed64) + TEST_TRIM_ONE_PRIMITIVE_FIELD(optional_float) + TEST_TRIM_ONE_PRIMITIVE_FIELD(optional_double) + TEST_TRIM_ONE_PRIMITIVE_FIELD(optional_bool) + TEST_TRIM_ONE_PRIMITIVE_FIELD(optional_string) + TEST_TRIM_ONE_PRIMITIVE_FIELD(optional_bytes) + TEST_TRIM_ONE_PRIMITIVE_FIELD(optional_nested_enum) + TEST_TRIM_ONE_PRIMITIVE_FIELD(optional_foreign_enum) + TEST_TRIM_ONE_PRIMITIVE_FIELD(optional_import_enum) +#undef TEST_TRIM_ONE_PRIMITIVE_FIELD + +#define TEST_TRIM_ONE_FIELD(field_name) \ + { \ + TestAllTypes msg; \ + TestUtil::SetAllFields(&msg); \ + TestAllTypes tmp; \ + *tmp.mutable_##field_name() = msg.field_name(); \ + FieldMask mask; \ + mask.add_paths(#field_name); \ + FieldMaskUtil::TrimMessage(mask, &msg); \ + EXPECT_EQ(tmp.DebugString(), msg.DebugString()); \ + } + TEST_TRIM_ONE_FIELD(optional_nested_message) + TEST_TRIM_ONE_FIELD(optional_foreign_message) + TEST_TRIM_ONE_FIELD(optional_import_message) + + TEST_TRIM_ONE_FIELD(repeated_int32) + TEST_TRIM_ONE_FIELD(repeated_int64) + TEST_TRIM_ONE_FIELD(repeated_uint32) + TEST_TRIM_ONE_FIELD(repeated_uint64) + TEST_TRIM_ONE_FIELD(repeated_sint32) + TEST_TRIM_ONE_FIELD(repeated_sint64) + TEST_TRIM_ONE_FIELD(repeated_fixed32) + TEST_TRIM_ONE_FIELD(repeated_fixed64) + TEST_TRIM_ONE_FIELD(repeated_sfixed32) + TEST_TRIM_ONE_FIELD(repeated_sfixed64) + TEST_TRIM_ONE_FIELD(repeated_float) + TEST_TRIM_ONE_FIELD(repeated_double) + TEST_TRIM_ONE_FIELD(repeated_bool) + TEST_TRIM_ONE_FIELD(repeated_string) + TEST_TRIM_ONE_FIELD(repeated_bytes) + TEST_TRIM_ONE_FIELD(repeated_nested_message) + TEST_TRIM_ONE_FIELD(repeated_foreign_message) + TEST_TRIM_ONE_FIELD(repeated_import_message) + TEST_TRIM_ONE_FIELD(repeated_nested_enum) + TEST_TRIM_ONE_FIELD(repeated_foreign_enum) + TEST_TRIM_ONE_FIELD(repeated_import_enum) +#undef TEST_TRIM_ONE_FIELD + + // Test trim nested fields. + NestedTestAllTypes nested_msg; + nested_msg.mutable_child()->mutable_payload()->set_optional_int32(1234); + nested_msg.mutable_child() + ->mutable_child() + ->mutable_payload() + ->set_optional_int32(5678); + NestedTestAllTypes trimmed_msg(nested_msg); + FieldMask mask; + FieldMaskUtil::FromString("child.payload", &mask); + FieldMaskUtil::TrimMessage(mask, &trimmed_msg); + EXPECT_EQ(1234, trimmed_msg.child().payload().optional_int32()); + EXPECT_EQ(0, trimmed_msg.child().child().payload().optional_int32()); + + trimmed_msg = nested_msg; + FieldMaskUtil::FromString("child.child.payload", &mask); + FieldMaskUtil::TrimMessage(mask, &trimmed_msg); + EXPECT_EQ(0, trimmed_msg.child().payload().optional_int32()); + EXPECT_EQ(5678, trimmed_msg.child().child().payload().optional_int32()); + + trimmed_msg = nested_msg; + FieldMaskUtil::FromString("child", &mask); + FieldMaskUtil::TrimMessage(mask, &trimmed_msg); + EXPECT_EQ(1234, trimmed_msg.child().payload().optional_int32()); + EXPECT_EQ(5678, trimmed_msg.child().child().payload().optional_int32()); + + trimmed_msg = nested_msg; + FieldMaskUtil::FromString("child.child", &mask); + FieldMaskUtil::TrimMessage(mask, &trimmed_msg); + EXPECT_EQ(0, trimmed_msg.child().payload().optional_int32()); + EXPECT_EQ(5678, trimmed_msg.child().child().payload().optional_int32()); + + // Verify than an empty FieldMask trims nothing + TestAllTypes all_types_msg; + TestUtil::SetAllFields(&all_types_msg); + TestAllTypes trimmed_all_types(all_types_msg); + FieldMask empty_mask; + FieldMaskUtil::TrimMessage(empty_mask, &trimmed_all_types); + EXPECT_EQ(trimmed_all_types.DebugString(), all_types_msg.DebugString()); +} + } // namespace } // namespace util diff --git a/src/google/protobuf/util/internal/datapiece.cc b/src/google/protobuf/util/internal/datapiece.cc index 72c0aca6..ef8da427 100644 --- a/src/google/protobuf/util/internal/datapiece.cc +++ b/src/google/protobuf/util/internal/datapiece.cc @@ -329,9 +329,8 @@ bool DataPiece::DecodeBase64(StringPiece src, string* dest) const { // WebSafeBase64Escape does no padding by default. WebSafeBase64Escape(*dest, &encoded); // Remove trailing padding '=' characters before comparison. - StringPiece src_no_padding(src, 0, src.ends_with("=") - ? src.find_last_not_of('=') + 1 - : src.length()); + StringPiece src_no_padding = StringPiece(src).substr( + 0, src.ends_with("=") ? src.find_last_not_of('=') + 1 : src.length()); return encoded == src_no_padding; } return true; @@ -343,9 +342,8 @@ bool DataPiece::DecodeBase64(StringPiece src, string* dest) const { Base64Escape( reinterpret_cast<const unsigned char*>(dest->data()), dest->length(), &encoded, false); - StringPiece src_no_padding(src, 0, src.ends_with("=") - ? src.find_last_not_of('=') + 1 - : src.length()); + StringPiece src_no_padding = StringPiece(src).substr( + 0, src.ends_with("=") ? src.find_last_not_of('=') + 1 : src.length()); return encoded == src_no_padding; } return true; @@ -354,6 +352,26 @@ bool DataPiece::DecodeBase64(StringPiece src, string* dest) const { return false; } +void DataPiece::InternalCopy(const DataPiece& other) { + type_ = other.type_; + switch (type_) { + case TYPE_INT32: + case TYPE_INT64: + case TYPE_UINT32: + case TYPE_UINT64: + case TYPE_DOUBLE: + case TYPE_FLOAT: + case TYPE_BOOL: + case TYPE_ENUM: + case TYPE_NULL: + case TYPE_BYTES: + case TYPE_STRING: { + str_ = other.str_; + break; + } + } +} + } // namespace converter } // namespace util } // namespace protobuf diff --git a/src/google/protobuf/util/internal/datapiece.h b/src/google/protobuf/util/internal/datapiece.h index 8b2e35d3..e82cdbac 100644 --- a/src/google/protobuf/util/internal/datapiece.h +++ b/src/google/protobuf/util/internal/datapiece.h @@ -92,10 +92,11 @@ class LIBPROTOBUF_EXPORT DataPiece { : type_(TYPE_BYTES), str_(StringPiecePod::CreateFromStringPiece(value)), use_strict_base64_decoding_(use_strict_base64_decoding) {} - DataPiece(const DataPiece& r) : type_(r.type_), str_(r.str_) {} + + DataPiece(const DataPiece& r) : type_(r.type_) { InternalCopy(r); } + DataPiece& operator=(const DataPiece& x) { - type_ = x.type_; - str_ = x.str_; + InternalCopy(x); return *this; } @@ -171,6 +172,9 @@ class LIBPROTOBUF_EXPORT DataPiece { // Decodes a base64 string. Returns true on success. bool DecodeBase64(StringPiece src, string* dest) const; + // Helper function to initialize this DataPiece with 'other'. + void InternalCopy(const DataPiece& other); + // Data type for this piece of data. Type type_; diff --git a/src/google/protobuf/util/internal/json_escaping.cc b/src/google/protobuf/util/internal/json_escaping.cc index 24bd554e..06d2791b 100644 --- a/src/google/protobuf/util/internal/json_escaping.cc +++ b/src/google/protobuf/util/internal/json_escaping.cc @@ -255,7 +255,7 @@ StringPiece ToHex(uint16 cp, char* buffer) { buffer[3] = kHex[cp & 0x0f]; cp >>= 4; buffer[2] = kHex[cp & 0x0f]; - return StringPiece(buffer, 0, 6); + return StringPiece(buffer).substr(0, 6); } // Stores the 32-bit unicode code point as its hexadecimal digits in buffer diff --git a/src/google/protobuf/util/internal/object_writer.h b/src/google/protobuf/util/internal/object_writer.h index 9f07363d..5781aa1e 100644 --- a/src/google/protobuf/util/internal/object_writer.h +++ b/src/google/protobuf/util/internal/object_writer.h @@ -101,6 +101,7 @@ class LIBPROTOBUF_EXPORT ObjectWriter { // Renders a Null value. virtual ObjectWriter* RenderNull(StringPiece name) = 0; + // Renders a DataPiece object to a ObjectWriter. static void RenderDataPieceTo(const DataPiece& data, StringPiece name, ObjectWriter* ow); diff --git a/src/google/protobuf/util/internal/proto_writer.cc b/src/google/protobuf/util/internal/proto_writer.cc index 36b79410..18cc1233 100644 --- a/src/google/protobuf/util/internal/proto_writer.cc +++ b/src/google/protobuf/util/internal/proto_writer.cc @@ -293,10 +293,14 @@ ProtoWriter::ProtoElement::ProtoElement(const TypeInfo* typeinfo, ow_(enclosing), parent_field_(NULL), typeinfo_(typeinfo), + proto3_(type.syntax() == google::protobuf::SYNTAX_PROTO3), type_(type), - required_fields_(GetRequiredFields(type)), size_index_(-1), - array_index_(-1) {} + array_index_(-1) { + if (!proto3_) { + required_fields_ = GetRequiredFields(type_); + } +} ProtoWriter::ProtoElement::ProtoElement(ProtoWriter::ProtoElement* parent, const google::protobuf::Field* field, @@ -306,6 +310,7 @@ ProtoWriter::ProtoElement::ProtoElement(ProtoWriter::ProtoElement* parent, ow_(this->parent()->ow_), parent_field_(field), typeinfo_(this->parent()->typeinfo_), + proto3_(this->parent()->proto3_), type_(type), size_index_( !is_list && field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE @@ -316,12 +321,15 @@ ProtoWriter::ProtoElement::ProtoElement(ProtoWriter::ProtoElement* parent, 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 +342,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 (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. diff --git a/src/google/protobuf/util/internal/proto_writer.h b/src/google/protobuf/util/internal/proto_writer.h index 957565e7..ffb8f60e 100644 --- a/src/google/protobuf/util/internal/proto_writer.h +++ b/src/google/protobuf/util/internal/proto_writer.h @@ -117,6 +117,7 @@ class LIBPROTOBUF_EXPORT ProtoWriter : public StructuredObjectWriter { return RenderDataPiece(name, DataPiece::NullData()); } + // Renders a DataPiece 'value' into a field whose wire type is determined // from the given field 'name'. virtual ProtoWriter* RenderDataPiece(StringPiece name, @@ -198,6 +199,9 @@ class LIBPROTOBUF_EXPORT ProtoWriter : public StructuredObjectWriter { // TypeInfo to lookup types. const TypeInfo* typeinfo_; + // Whether the root type is a proto3 or not. + bool proto3_; + // Additional variables if this element is a message: // (Root element is always a message). // type_ : the type of this element. diff --git a/src/google/protobuf/util/internal/protostream_objectsource.cc b/src/google/protobuf/util/internal/protostream_objectsource.cc index 1f3781a4..0048d75b 100644 --- a/src/google/protobuf/util/internal/protostream_objectsource.cc +++ b/src/google/protobuf/util/internal/protostream_objectsource.cc @@ -121,7 +121,8 @@ ProtoStreamObjectSource::ProtoStreamObjectSource( type_(type), use_lower_camel_for_enums_(false), recursion_depth_(0), - max_recursion_depth_(kDefaultMaxRecursionDepth) { + max_recursion_depth_(kDefaultMaxRecursionDepth), + render_unknown_fields_(false) { GOOGLE_LOG_IF(DFATAL, stream == NULL) << "Input stream is NULL."; } @@ -134,7 +135,8 @@ ProtoStreamObjectSource::ProtoStreamObjectSource( type_(type), use_lower_camel_for_enums_(false), recursion_depth_(0), - max_recursion_depth_(kDefaultMaxRecursionDepth) { + max_recursion_depth_(kDefaultMaxRecursionDepth), + render_unknown_fields_(false) { GOOGLE_LOG_IF(DFATAL, stream == NULL) << "Input stream is NULL."; } @@ -184,6 +186,7 @@ Status ProtoStreamObjectSource::WriteMessage(const google::protobuf::Type& type, string field_name; // last_tag set to dummy value that is different from tag. uint32 tag = stream_->ReadTag(), last_tag = tag + 1; + google::protobuf::UnknownFieldSet unknown_fields; if (include_start_and_end) { ow->StartObject(name); @@ -199,7 +202,8 @@ Status ProtoStreamObjectSource::WriteMessage(const google::protobuf::Type& type, if (field == NULL) { // If we didn't find a field, skip this unknown tag. // TODO(wpoon): Check return boolean value. - WireFormat::SkipField(stream_, tag, NULL); + WireFormat::SkipField(stream_, tag, + render_unknown_fields_ ? &unknown_fields : NULL); tag = stream_->ReadTag(); continue; } @@ -221,6 +225,8 @@ Status ProtoStreamObjectSource::WriteMessage(const google::protobuf::Type& type, tag = stream_->ReadTag(); } } + + if (include_start_and_end) { ow->EndObject(); } diff --git a/src/google/protobuf/util/internal/protostream_objectsource.h b/src/google/protobuf/util/internal/protostream_objectsource.h index d7d4347b..243f85b2 100644 --- a/src/google/protobuf/util/internal/protostream_objectsource.h +++ b/src/google/protobuf/util/internal/protostream_objectsource.h @@ -117,6 +117,7 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource { max_recursion_depth_ = max_depth; } + protected: // Writes a proto2 Message to the ObjectWriter. When the given end_tag is // found this method will complete, allowing it to be used for parsing both @@ -287,6 +288,9 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource { // Maximum allowed recursion depth. int max_recursion_depth_; + // Whether to render unknown fields. + bool render_unknown_fields_; + GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoStreamObjectSource); }; diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.cc b/src/google/protobuf/util/internal/protostream_objectwriter.cc index 97a7909a..8fa58a6f 100644 --- a/src/google/protobuf/util/internal/protostream_objectwriter.cc +++ b/src/google/protobuf/util/internal/protostream_objectwriter.cc @@ -384,6 +384,9 @@ ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter* enclosing, if (item_type_ == ANY) { any_.reset(new AnyWriter(ow_)); } + if (item_type == MAP) { + map_keys_.reset(new hash_set<string>); + } } ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter::Item* parent, @@ -398,11 +401,14 @@ ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter::Item* parent, if (item_type == ANY) { any_.reset(new AnyWriter(ow_)); } + if (item_type == MAP) { + map_keys_.reset(new hash_set<string>); + } } bool ProtoStreamObjectWriter::Item::InsertMapKeyIfNotPresent( StringPiece map_key) { - return InsertIfNotPresent(&map_keys_, map_key.ToString()); + return InsertIfNotPresent(map_keys_.get(), map_key.ToString()); } ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartObject( @@ -1000,6 +1006,7 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece( DataPiece(name, use_strict_base64_decoding())); field = Lookup("value"); if (field == NULL) { + Pop(); GOOGLE_LOG(DFATAL) << "Map does not have a value field."; return this; } diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.h b/src/google/protobuf/util/internal/protostream_objectwriter.h index e1162d43..75e3d67d 100644 --- a/src/google/protobuf/util/internal/protostream_objectwriter.h +++ b/src/google/protobuf/util/internal/protostream_objectwriter.h @@ -231,7 +231,7 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public ProtoWriter { // Set of map keys already seen for the type_. Used to validate incoming // messages so no map key appears more than once. - hash_set<string> map_keys_; + google::protobuf::scoped_ptr<hash_set<string> > map_keys_; // Conveys whether this Item is a placeholder or not. Placeholder items are // pushed to stack to account for special types. @@ -249,19 +249,19 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public ProtoWriter { strings::ByteSink* output, ErrorListener* listener); // Returns true if the field is a map. - bool IsMap(const google::protobuf::Field& field); + inline bool IsMap(const google::protobuf::Field& field); // Returns true if the field is an any. - bool IsAny(const google::protobuf::Field& field); + inline bool IsAny(const google::protobuf::Field& field); // Returns true if the field is google.protobuf.Struct. - bool IsStruct(const google::protobuf::Field& field); + inline bool IsStruct(const google::protobuf::Field& field); // Returns true if the field is google.protobuf.Value. - bool IsStructValue(const google::protobuf::Field& field); + inline bool IsStructValue(const google::protobuf::Field& field); // Returns true if the field is google.protobuf.ListValue. - bool IsStructListValue(const google::protobuf::Field& field); + inline bool IsStructListValue(const google::protobuf::Field& field); // Renders google.protobuf.Value in struct.proto. It picks the right oneof // type based on value's type. diff --git a/src/google/protobuf/util/internal/utility.cc b/src/google/protobuf/util/internal/utility.cc index ee7a51fc..5f613e77 100644 --- a/src/google/protobuf/util/internal/utility.cc +++ b/src/google/protobuf/util/internal/utility.cc @@ -52,7 +52,7 @@ const StringPiece SkipWhiteSpace(StringPiece str) { for (i = 0; i < str.size() && isspace(str[i]); ++i) { } GOOGLE_DCHECK(i == str.size() || !isspace(str[i])); - return StringPiece(str, i); + return str.substr(i); } } // namespace @@ -128,8 +128,12 @@ string GetStringFromAny(const google::protobuf::Any& any) { } const StringPiece GetTypeWithoutUrl(StringPiece type_url) { - size_t idx = type_url.rfind('/'); - return type_url.substr(idx + 1); + if (type_url.size() > kTypeUrlSize && type_url[kTypeUrlSize] == '/') { + return type_url.substr(kTypeUrlSize + 1); + } else { + size_t idx = type_url.rfind('/'); + return type_url.substr(idx + 1); + } } const string GetFullTypeWithUrl(StringPiece simple_type) { diff --git a/src/google/protobuf/util/internal/utility.h b/src/google/protobuf/util/internal/utility.h index 33df8eda..26fed444 100644 --- a/src/google/protobuf/util/internal/utility.h +++ b/src/google/protobuf/util/internal/utility.h @@ -64,6 +64,10 @@ class EnumValue; namespace protobuf { namespace util { namespace converter { + +// Size of "type.googleapis.com" +static const int64 kTypeUrlSize = 19; + // Finds the tech option identified by option_name. Parses the boolean value and // returns it. // When the option with the given name is not found, default_value is returned. diff --git a/src/google/protobuf/util/json_format_proto3.proto b/src/google/protobuf/util/json_format_proto3.proto index a1e24c18..3835b30e 100644 --- a/src/google/protobuf/util/json_format_proto3.proto +++ b/src/google/protobuf/util/json_format_proto3.proto @@ -39,6 +39,7 @@ import "google/protobuf/wrappers.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/any.proto"; import "google/protobuf/field_mask.proto"; +import "google/protobuf/unittest.proto"; enum EnumType { FOO = 0; @@ -174,3 +175,7 @@ message TestBoolValue { message TestCustomJsonName { int32 value = 1 [json_name = "@value"]; } + +message TestExtensions { + .protobuf_unittest.TestAllExtensions extensions = 1; +} diff --git a/src/google/protobuf/util/message_differencer.cc b/src/google/protobuf/util/message_differencer.cc index fe8119bf..fc55c2b9 100644 --- a/src/google/protobuf/util/message_differencer.cc +++ b/src/google/protobuf/util/message_differencer.cc @@ -474,7 +474,10 @@ bool MessageDifferencer::Compare( // Retrieve all the set fields, including extensions. vector<const FieldDescriptor*> message1_fields; + message1_fields.reserve(1 + message1.GetDescriptor()->field_count()); + vector<const FieldDescriptor*> message2_fields; + message2_fields.reserve(1 + message2.GetDescriptor()->field_count()); reflection1->ListFields(message1, &message1_fields); reflection2->ListFields(message2, &message2_fields); diff --git a/src/google/protobuf/util/message_differencer.h b/src/google/protobuf/util/message_differencer.h index 3ea74e67..1abbfcba 100644 --- a/src/google/protobuf/util/message_differencer.h +++ b/src/google/protobuf/util/message_differencer.h @@ -570,6 +570,12 @@ class LIBPROTOBUF_EXPORT MessageDifferencer { // any differences found in human-readable form to the supplied // ZeroCopyOutputStream or Printer. If a printer is used, the delimiter // *must* be '$'. + // + // WARNING: this reporter does not necessarily flush its output until it is + // destroyed. As a result, it is not safe to assume the output is valid or + // complete until after you destroy the reporter. For example, if you use a + // StreamReporter to write to a StringOutputStream, the target string may + // contain uninitialized data until the reporter is destroyed. class LIBPROTOBUF_EXPORT StreamReporter : public Reporter { public: explicit StreamReporter(io::ZeroCopyOutputStream* output); |