aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/google/protobuf/util
diff options
context:
space:
mode:
authorGravatar Adam Cozzette <acozzette@google.com>2016-06-29 15:23:27 -0700
committerGravatar Adam Cozzette <acozzette@google.com>2016-06-29 15:38:03 -0700
commitd64a2d9941c36a7bc2a7959ea10ab8363192ac14 (patch)
tree52330d146ad63d3d70f3baade00d5d1fea8f5e0c /src/google/protobuf/util
parentc18aa7795a2e02ef700ff8b039d94ecdcc33432f (diff)
Integrated internal changes from Google
This includes all internal changes from around May 20 to now.
Diffstat (limited to 'src/google/protobuf/util')
-rw-r--r--src/google/protobuf/util/field_mask_util.cc54
-rw-r--r--src/google/protobuf/util/field_mask_util.h12
-rw-r--r--src/google/protobuf/util/field_mask_util_test.cc115
-rw-r--r--src/google/protobuf/util/internal/datapiece.cc30
-rw-r--r--src/google/protobuf/util/internal/datapiece.h10
-rw-r--r--src/google/protobuf/util/internal/json_escaping.cc2
-rw-r--r--src/google/protobuf/util/internal/object_writer.h1
-rw-r--r--src/google/protobuf/util/internal/proto_writer.cc30
-rw-r--r--src/google/protobuf/util/internal/proto_writer.h4
-rw-r--r--src/google/protobuf/util/internal/protostream_objectsource.cc12
-rw-r--r--src/google/protobuf/util/internal/protostream_objectsource.h4
-rw-r--r--src/google/protobuf/util/internal/protostream_objectwriter.cc9
-rw-r--r--src/google/protobuf/util/internal/protostream_objectwriter.h12
-rw-r--r--src/google/protobuf/util/internal/utility.cc10
-rw-r--r--src/google/protobuf/util/internal/utility.h4
-rw-r--r--src/google/protobuf/util/json_format_proto3.proto5
-rw-r--r--src/google/protobuf/util/message_differencer.cc3
-rw-r--r--src/google/protobuf/util/message_differencer.h6
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);