aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/google/protobuf/util
diff options
context:
space:
mode:
authorGravatar Feng Xiao <xfxyjwf@gmail.com>2015-12-11 17:09:20 -0800
committerGravatar Feng Xiao <xfxyjwf@gmail.com>2015-12-11 17:10:28 -0800
commite841bac4fcf47f809e089a70d5f84ac37b3883df (patch)
treed25dc5fc814db182c04c5f276ff1a609c5965a5a /src/google/protobuf/util
parent99a6a95c751a28a3cc33dd2384959179f83f682c (diff)
Down-integrate from internal code base.
Diffstat (limited to 'src/google/protobuf/util')
-rw-r--r--src/google/protobuf/util/field_comparator.h2
-rw-r--r--src/google/protobuf/util/field_comparator_test.cc7
-rw-r--r--src/google/protobuf/util/internal/datapiece.cc74
-rw-r--r--src/google/protobuf/util/internal/datapiece.h3
-rw-r--r--src/google/protobuf/util/internal/default_value_objectwriter.cc90
-rw-r--r--src/google/protobuf/util/internal/default_value_objectwriter.h23
-rw-r--r--src/google/protobuf/util/internal/default_value_objectwriter_test.cc35
-rw-r--r--src/google/protobuf/util/internal/error_listener.h2
-rw-r--r--src/google/protobuf/util/internal/json_objectwriter_test.cc205
-rw-r--r--src/google/protobuf/util/internal/json_stream_parser.cc2
-rw-r--r--src/google/protobuf/util/internal/json_stream_parser_test.cc1
-rw-r--r--src/google/protobuf/util/internal/object_writer.h5
-rw-r--r--src/google/protobuf/util/internal/proto_writer.cc744
-rw-r--r--src/google/protobuf/util/internal/proto_writer.h315
-rw-r--r--src/google/protobuf/util/internal/protostream_objectsource.cc187
-rw-r--r--src/google/protobuf/util/internal/protostream_objectsource.h15
-rw-r--r--src/google/protobuf/util/internal/protostream_objectsource_test.cc10
-rw-r--r--src/google/protobuf/util/internal/protostream_objectwriter.cc1560
-rw-r--r--src/google/protobuf/util/internal/protostream_objectwriter.h318
-rw-r--r--src/google/protobuf/util/internal/protostream_objectwriter_test.cc126
-rw-r--r--src/google/protobuf/util/internal/testdata/books.proto2
-rw-r--r--src/google/protobuf/util/internal/testdata/default_value.proto7
-rw-r--r--src/google/protobuf/util/internal/testdata/default_value_test.proto7
-rw-r--r--src/google/protobuf/util/internal/type_info.cc3
-rw-r--r--src/google/protobuf/util/internal/utility.cc29
-rw-r--r--src/google/protobuf/util/internal/utility.h5
-rw-r--r--src/google/protobuf/util/json_format_proto3.proto9
-rw-r--r--src/google/protobuf/util/message_differencer.cc22
-rw-r--r--src/google/protobuf/util/message_differencer.h10
-rwxr-xr-xsrc/google/protobuf/util/message_differencer_unittest.cc24
-rw-r--r--src/google/protobuf/util/type_resolver_util.cc45
-rw-r--r--src/google/protobuf/util/type_resolver_util_test.cc14
32 files changed, 2277 insertions, 1624 deletions
diff --git a/src/google/protobuf/util/field_comparator.h b/src/google/protobuf/util/field_comparator.h
index ee676265..8b83c69f 100644
--- a/src/google/protobuf/util/field_comparator.h
+++ b/src/google/protobuf/util/field_comparator.h
@@ -93,7 +93,7 @@ class LIBPROTOBUF_EXPORT FieldComparator {
// Basic implementation of FieldComparator. Supports four modes of floating
// point value comparison: exact, approximate using MathUtil::AlmostEqual
-// method, and arbitrarilly precise using MathUtil::WithinFracionOrMargin.
+// method, and arbitrarilly precise using MathUtil::WithinFractionOrMargin.
class LIBPROTOBUF_EXPORT DefaultFieldComparator : public FieldComparator {
public:
enum FloatComparison {
diff --git a/src/google/protobuf/util/field_comparator_test.cc b/src/google/protobuf/util/field_comparator_test.cc
index d3d34602..23f7d51d 100644
--- a/src/google/protobuf/util/field_comparator_test.cc
+++ b/src/google/protobuf/util/field_comparator_test.cc
@@ -35,7 +35,12 @@
#include <google/protobuf/unittest.pb.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/stubs/mathutil.h>
-
+// This gtest header is put after mathutil.h intentionally. We have to do
+// this because mathutil.h includes mathlimits.h which requires cmath not
+// being included to compile on some versions of gcc:
+// https://github.com/google/protobuf/blob/818c5eee08840355d70d2f3bdf1a2f17986a5e70/src/google/protobuf/stubs/mathlimits.h#L48
+// and the opensource version gtest.h header includes cmath transitively
+// somehow.
#include <gtest/gtest.h>
namespace google {
diff --git a/src/google/protobuf/util/internal/datapiece.cc b/src/google/protobuf/util/internal/datapiece.cc
index ea360798..b557429f 100644
--- a/src/google/protobuf/util/internal/datapiece.cc
+++ b/src/google/protobuf/util/internal/datapiece.cc
@@ -35,8 +35,8 @@
#include <google/protobuf/descriptor.h>
#include <google/protobuf/util/internal/utility.h>
#include <google/protobuf/stubs/strutil.h>
-#include <google/protobuf/stubs/mathutil.h>
#include <google/protobuf/stubs/mathlimits.h>
+#include <google/protobuf/stubs/mathutil.h>
namespace google {
namespace protobuf {
@@ -57,13 +57,8 @@ inline Status InvalidArgument(StringPiece value_str) {
return Status(util::error::INVALID_ARGUMENT, value_str);
}
-// For general conversion between
-// int32, int64, uint32, uint64, double and float
-// except conversion between double and float.
template <typename To, typename From>
-StatusOr<To> NumberConvertAndCheck(From before) {
- if (::google::protobuf::internal::is_same<From, To>::value) return before;
- To after = static_cast<To>(before);
+StatusOr<To> ValidateNumberConversion(To after, From before) {
if (after == before &&
MathUtil::Sign<From>(before) == MathUtil::Sign<To>(after)) {
return after;
@@ -76,6 +71,27 @@ StatusOr<To> NumberConvertAndCheck(From before) {
}
}
+// For general conversion between
+// int32, int64, uint32, uint64, double and float
+// except conversion between double and float.
+template <typename To, typename From>
+StatusOr<To> NumberConvertAndCheck(From before) {
+ if (::google::protobuf::internal::is_same<From, To>::value) return before;
+
+ To after = static_cast<To>(before);
+ return ValidateNumberConversion(after, before);
+}
+
+// For conversion to integer types (int32, int64, uint32, uint64) from floating
+// point types (double, float) only.
+template <typename To, typename From>
+StatusOr<To> FloatingPointToIntConvertAndCheck(From before) {
+ if (::google::protobuf::internal::is_same<From, To>::value) return before;
+
+ To after = static_cast<To>(before);
+ return ValidateNumberConversion(after, before);
+}
+
// For conversion between double and float only.
template <typename To, typename From>
StatusOr<To> FloatingPointConvertAndCheck(From before) {
@@ -96,30 +112,50 @@ StatusOr<To> FloatingPointConvertAndCheck(From before) {
} // namespace
StatusOr<int32> DataPiece::ToInt32() const {
- if (type_ == TYPE_STRING) {
- return StringToNumber<int32>(safe_strto32);
- }
+ if (type_ == TYPE_STRING) return StringToNumber<int32>(safe_strto32);
+
+ if (type_ == TYPE_DOUBLE)
+ return FloatingPointToIntConvertAndCheck<int32, double>(double_);
+
+ if (type_ == TYPE_FLOAT)
+ return FloatingPointToIntConvertAndCheck<int32, float>(float_);
+
return GenericConvert<int32>();
}
StatusOr<uint32> DataPiece::ToUint32() const {
- if (type_ == TYPE_STRING) {
- return StringToNumber<uint32>(safe_strtou32);
- }
+ if (type_ == TYPE_STRING) return StringToNumber<uint32>(safe_strtou32);
+
+ if (type_ == TYPE_DOUBLE)
+ return FloatingPointToIntConvertAndCheck<uint32, double>(double_);
+
+ if (type_ == TYPE_FLOAT)
+ return FloatingPointToIntConvertAndCheck<uint32, float>(float_);
+
return GenericConvert<uint32>();
}
StatusOr<int64> DataPiece::ToInt64() const {
- if (type_ == TYPE_STRING) {
- return StringToNumber<int64>(safe_strto64);
- }
+ if (type_ == TYPE_STRING) return StringToNumber<int64>(safe_strto64);
+
+ if (type_ == TYPE_DOUBLE)
+ return FloatingPointToIntConvertAndCheck<int64, double>(double_);
+
+ if (type_ == TYPE_FLOAT)
+ return FloatingPointToIntConvertAndCheck<int64, float>(float_);
+
return GenericConvert<int64>();
}
StatusOr<uint64> DataPiece::ToUint64() const {
- if (type_ == TYPE_STRING) {
- return StringToNumber<uint64>(safe_strtou64);
- }
+ if (type_ == TYPE_STRING) return StringToNumber<uint64>(safe_strtou64);
+
+ if (type_ == TYPE_DOUBLE)
+ return FloatingPointToIntConvertAndCheck<uint64, double>(double_);
+
+ if (type_ == TYPE_FLOAT)
+ return FloatingPointToIntConvertAndCheck<uint64, float>(float_);
+
return GenericConvert<uint64>();
}
diff --git a/src/google/protobuf/util/internal/datapiece.h b/src/google/protobuf/util/internal/datapiece.h
index 2ab3fa88..f22bfe70 100644
--- a/src/google/protobuf/util/internal/datapiece.h
+++ b/src/google/protobuf/util/internal/datapiece.h
@@ -98,7 +98,8 @@ class LIBPROTOBUF_EXPORT DataPiece {
static DataPiece NullData() { return DataPiece(TYPE_NULL, 0); }
- virtual ~DataPiece() {}
+ virtual ~DataPiece() {
+ }
// Accessors
Type type() const { return type_; }
diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.cc b/src/google/protobuf/util/internal/default_value_objectwriter.cc
index 97b248ff..a63e560d 100644
--- a/src/google/protobuf/util/internal/default_value_objectwriter.cc
+++ b/src/google/protobuf/util/internal/default_value_objectwriter.cc
@@ -33,6 +33,7 @@
#include <google/protobuf/stubs/hash.h>
#include <google/protobuf/util/internal/constants.h>
+#include <google/protobuf/util/internal/utility.h>
#include <google/protobuf/stubs/map_util.h>
namespace google {
@@ -42,13 +43,25 @@ using util::Status;
using util::StatusOr;
namespace converter {
+namespace {
+// Helper function to convert string value to given data type by calling the
+// passed converter function on the DataPiece created from "value" argument.
+// If value is empty or if conversion fails, the default_value is returned.
+template <typename T>
+T ConvertTo(StringPiece value, StatusOr<T> (DataPiece::*converter_fn)() const,
+ T default_value) {
+ if (value.empty()) return default_value;
+ StatusOr<T> result = (DataPiece(value).*converter_fn)();
+ return result.ok() ? result.ValueOrDie() : default_value;
+}
+} // namespace
+
DefaultValueObjectWriter::DefaultValueObjectWriter(
TypeResolver* type_resolver, const google::protobuf::Type& type,
ObjectWriter* ow)
: typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
own_typeinfo_(true),
type_(type),
- disable_normalize_(false),
current_(NULL),
root_(NULL),
ow_(ow) {}
@@ -165,12 +178,6 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::RenderNull(
return this;
}
-DefaultValueObjectWriter*
-DefaultValueObjectWriter::DisableCaseNormalizationForNextKey() {
- disable_normalize_ = true;
- return this;
-}
-
DefaultValueObjectWriter::Node::Node(const string& name,
const google::protobuf::Type* type,
NodeKind kind, const DataPiece& data,
@@ -178,7 +185,6 @@ DefaultValueObjectWriter::Node::Node(const string& name,
: name_(name),
type_(type),
kind_(kind),
- disable_normalize_(false),
is_any_(false),
data_(data),
is_placeholder_(is_placeholder) {}
@@ -198,10 +204,6 @@ DefaultValueObjectWriter::Node* DefaultValueObjectWriter::Node::FindChild(
}
void DefaultValueObjectWriter::Node::WriteTo(ObjectWriter* ow) {
- if (disable_normalize_) {
- ow->DisableCaseNormalizationForNextKey();
- }
-
if (kind_ == PRIMITIVE) {
ObjectWriter::RenderDataPieceTo(data_, name_, ow);
return;
@@ -324,6 +326,7 @@ void DefaultValueObjectWriter::Node::PopulateChildren(
}
}
}
+
if (!is_map &&
field.cardinality() ==
google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
@@ -336,11 +339,11 @@ void DefaultValueObjectWriter::Node::PopulateChildren(
// If the child field is of primitive type, sets its data to the default
// value of its type.
- google::protobuf::scoped_ptr<Node> child(
- new Node(field.json_name(), field_type, kind,
- kind == PRIMITIVE ? CreateDefaultDataPieceForField(field)
- : DataPiece::NullData(),
- true));
+ google::protobuf::scoped_ptr<Node> child(new Node(
+ field.json_name(), field_type, kind,
+ kind == PRIMITIVE ? CreateDefaultDataPieceForField(field, typeinfo)
+ : DataPiece::NullData(),
+ true));
new_children.push_back(child.release());
}
// Adds all leftover nodes in children_ to the beginning of new_child.
@@ -363,41 +366,68 @@ void DefaultValueObjectWriter::MaybePopulateChildrenOfAny(Node* node) {
}
}
+DataPiece DefaultValueObjectWriter::FindEnumDefault(
+ const google::protobuf::Field& field, const TypeInfo* typeinfo) {
+ if (!field.default_value().empty()) return DataPiece(field.default_value());
+
+ const google::protobuf::Enum* enum_type =
+ typeinfo->GetEnumByTypeUrl(field.type_url());
+ if (!enum_type) {
+ GOOGLE_LOG(WARNING) << "Could not find enum with type '" << field.type_url()
+ << "'";
+ return DataPiece::NullData();
+ }
+ // We treat the first value as the default if none is specified.
+ return enum_type->enumvalue_size() > 0
+ ? DataPiece(enum_type->enumvalue(0).name())
+ : DataPiece::NullData();
+}
+
DataPiece DefaultValueObjectWriter::CreateDefaultDataPieceForField(
- const google::protobuf::Field& field) {
+ const google::protobuf::Field& field, const TypeInfo* typeinfo) {
switch (field.kind()) {
case google::protobuf::Field_Kind_TYPE_DOUBLE: {
- return DataPiece(static_cast<double>(0));
+ return DataPiece(ConvertTo<double>(
+ field.default_value(), &DataPiece::ToDouble, static_cast<double>(0)));
}
case google::protobuf::Field_Kind_TYPE_FLOAT: {
- return DataPiece(static_cast<float>(0));
+ return DataPiece(ConvertTo<float>(
+ field.default_value(), &DataPiece::ToFloat, static_cast<float>(0)));
}
case google::protobuf::Field_Kind_TYPE_INT64:
case google::protobuf::Field_Kind_TYPE_SINT64:
case google::protobuf::Field_Kind_TYPE_SFIXED64: {
- return DataPiece(static_cast<int64>(0));
+ return DataPiece(ConvertTo<int64>(
+ field.default_value(), &DataPiece::ToInt64, static_cast<int64>(0)));
}
case google::protobuf::Field_Kind_TYPE_UINT64:
case google::protobuf::Field_Kind_TYPE_FIXED64: {
- return DataPiece(static_cast<uint64>(0));
+ return DataPiece(ConvertTo<uint64>(
+ field.default_value(), &DataPiece::ToUint64, static_cast<uint64>(0)));
}
case google::protobuf::Field_Kind_TYPE_INT32:
case google::protobuf::Field_Kind_TYPE_SINT32:
case google::protobuf::Field_Kind_TYPE_SFIXED32: {
- return DataPiece(static_cast<int32>(0));
+ return DataPiece(ConvertTo<int32>(
+ field.default_value(), &DataPiece::ToInt32, static_cast<int32>(0)));
}
case google::protobuf::Field_Kind_TYPE_BOOL: {
- return DataPiece(false);
+ return DataPiece(
+ ConvertTo<bool>(field.default_value(), &DataPiece::ToBool, false));
}
case google::protobuf::Field_Kind_TYPE_STRING: {
- return DataPiece(string());
+ return DataPiece(field.default_value());
}
case google::protobuf::Field_Kind_TYPE_BYTES: {
- return DataPiece("", false);
+ return DataPiece(field.default_value(), false);
}
case google::protobuf::Field_Kind_TYPE_UINT32:
case google::protobuf::Field_Kind_TYPE_FIXED32: {
- return DataPiece(static_cast<uint32>(0));
+ return DataPiece(ConvertTo<uint32>(
+ field.default_value(), &DataPiece::ToUint32, static_cast<uint32>(0)));
+ }
+ case google::protobuf::Field_Kind_TYPE_ENUM: {
+ return FindEnumDefault(field, typeinfo);
}
default: { return DataPiece::NullData(); }
}
@@ -408,7 +438,6 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject(
if (current_ == NULL) {
root_.reset(new Node(name.ToString(), &type_, OBJECT, DataPiece::NullData(),
false));
- root_->set_disable_normalize(GetAndResetDisableNormalize());
root_->PopulateChildren(typeinfo_);
current_ = root_.get();
return this;
@@ -428,7 +457,6 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject(
}
child->set_is_placeholder(false);
- child->set_disable_normalize(GetAndResetDisableNormalize());
if (child->kind() == OBJECT && child->number_of_children() == 0) {
child->PopulateChildren(typeinfo_);
}
@@ -454,21 +482,18 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::StartList(
if (current_ == NULL) {
root_.reset(
new Node(name.ToString(), &type_, LIST, DataPiece::NullData(), false));
- root_->set_disable_normalize(GetAndResetDisableNormalize());
current_ = root_.get();
return this;
}
MaybePopulateChildrenOfAny(current_);
Node* child = current_->FindChild(name);
if (child == NULL || child->kind() != LIST) {
- GOOGLE_LOG(WARNING) << "Cannot find field '" << name << "'.";
google::protobuf::scoped_ptr<Node> node(
new Node(name.ToString(), NULL, LIST, DataPiece::NullData(), false));
child = node.get();
current_->AddChild(node.release());
}
child->set_is_placeholder(false);
- child->set_disable_normalize(GetAndResetDisableNormalize());
stack_.push(current_);
current_ = child;
@@ -526,7 +551,6 @@ void DefaultValueObjectWriter::RenderDataPiece(StringPiece name,
} else {
child->set_data(data);
}
- child->set_disable_normalize(GetAndResetDisableNormalize());
}
} // namespace converter
diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.h b/src/google/protobuf/util/internal/default_value_objectwriter.h
index bcb526e8..695b9dd8 100644
--- a/src/google/protobuf/util/internal/default_value_objectwriter.h
+++ b/src/google/protobuf/util/internal/default_value_objectwriter.h
@@ -98,8 +98,6 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter {
virtual DefaultValueObjectWriter* RenderNull(StringPiece name);
- virtual DefaultValueObjectWriter* DisableCaseNormalizationForNextKey();
-
private:
enum NodeKind {
PRIMITIVE = 0,
@@ -149,10 +147,6 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter {
void set_data(const DataPiece& data) { data_ = data; }
- void set_disable_normalize(bool disable_normalize) {
- disable_normalize_ = disable_normalize;
- }
-
bool is_any() { return is_any_; }
void set_is_any(bool is_any) { is_any_ = is_any; }
@@ -176,8 +170,6 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter {
const google::protobuf::Type* type_;
// The kind of this node.
NodeKind kind_;
- // Whether to disable case normalization of the name.
- bool disable_normalize_;
// Whether this is a node for "Any".
bool is_any_;
// The data of this node when it is a leaf node.
@@ -201,16 +193,17 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter {
// Creates a DataPiece containing the default value of the type of the field.
static DataPiece CreateDefaultDataPieceForField(
- const google::protobuf::Field& field);
-
- // Returns disable_normalize_ and reset it to false.
- bool GetAndResetDisableNormalize() {
- return disable_normalize_ ? (disable_normalize_ = false, true) : false;
- }
+ const google::protobuf::Field& field, const TypeInfo* typeinfo);
// Adds or replaces the data_ of a primitive child node.
void RenderDataPiece(StringPiece name, const DataPiece& data);
+ // Returns the default enum value as a DataPiece, or the first enum value if
+ // there is no default. For proto3, where we cannot specify an explicit
+ // default, a zero value will always be returned.
+ static DataPiece FindEnumDefault(const google::protobuf::Field& field,
+ const TypeInfo* typeinfo);
+
// Type information for all the types used in the descriptor. Used to find
// google::protobuf::Type of nested messages/enums.
const TypeInfo* typeinfo_;
@@ -221,8 +214,6 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter {
// Holds copies of strings passed to RenderString.
vector<string*> string_values_;
- // Whether to disable case normalization of the next node.
- bool disable_normalize_;
// The current Node. Owned by its parents.
Node* current_;
// The root Node.
diff --git a/src/google/protobuf/util/internal/default_value_objectwriter_test.cc b/src/google/protobuf/util/internal/default_value_objectwriter_test.cc
index 237d0722..b7a537ab 100644
--- a/src/google/protobuf/util/internal/default_value_objectwriter_test.cc
+++ b/src/google/protobuf/util/internal/default_value_objectwriter_test.cc
@@ -43,21 +43,19 @@ namespace testing {
using google::protobuf::testing::DefaultValueTest;
-// Tests to cover some basic DefaultValueObjectWriter use cases. More tests are
-// in the marshalling_test.cc and translator_integration_test.cc.
-class DefaultValueObjectWriterTest
+// Base class for setting up required state for running default values tests on
+// different descriptors.
+class BaseDefaultValueObjectWriterTest
: public ::testing::TestWithParam<testing::TypeInfoSource> {
protected:
- DefaultValueObjectWriterTest()
+ explicit BaseDefaultValueObjectWriterTest(const Descriptor* descriptor)
: helper_(GetParam()), mock_(), expects_(&mock_) {
- helper_.ResetTypeInfo(DefaultValueTest::descriptor());
+ helper_.ResetTypeInfo(descriptor);
testing_.reset(helper_.NewDefaultValueWriter(
- string(kTypeServiceBaseUrl) + "/" +
- DefaultValueTest::descriptor()->full_name(),
- &mock_));
+ string(kTypeServiceBaseUrl) + "/" + descriptor->full_name(), &mock_));
}
- virtual ~DefaultValueObjectWriterTest() {}
+ ~BaseDefaultValueObjectWriterTest() override {}
TypeInfoTestHelper helper_;
MockObjectWriter mock_;
@@ -65,6 +63,15 @@ class DefaultValueObjectWriterTest
google::protobuf::scoped_ptr<DefaultValueObjectWriter> testing_;
};
+// Tests to cover some basic DefaultValueObjectWriter use cases. More tests are
+// in the marshalling_test.cc and translator_integration_test.cc.
+class DefaultValueObjectWriterTest : public BaseDefaultValueObjectWriterTest {
+ protected:
+ DefaultValueObjectWriterTest()
+ : BaseDefaultValueObjectWriterTest(DefaultValueTest::descriptor()) {}
+ ~DefaultValueObjectWriterTest() override {}
+};
+
INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest,
DefaultValueObjectWriterTest,
::testing::Values(
@@ -74,6 +81,8 @@ TEST_P(DefaultValueObjectWriterTest, Empty) {
// Set expectation
expects_.StartObject("")
->RenderDouble("doubleValue", 0.0)
+ ->StartList("repeatedDouble")
+ ->EndList()
->RenderFloat("floatValue", 0.0)
->RenderInt64("int64Value", 0)
->RenderUint64("uint64Value", 0)
@@ -82,6 +91,7 @@ TEST_P(DefaultValueObjectWriterTest, Empty) {
->RenderBool("boolValue", false)
->RenderString("stringValue", "")
->RenderBytes("bytesValue", "")
+ ->RenderString("enumValue", "ENUM_FIRST")
->EndObject();
// Actual testing
@@ -92,6 +102,8 @@ TEST_P(DefaultValueObjectWriterTest, NonDefaultDouble) {
// Set expectation
expects_.StartObject("")
->RenderDouble("doubleValue", 1.0)
+ ->StartList("repeatedDouble")
+ ->EndList()
->RenderFloat("floatValue", 0.0)
->RenderInt64("int64Value", 0)
->RenderUint64("uint64Value", 0)
@@ -99,6 +111,7 @@ TEST_P(DefaultValueObjectWriterTest, NonDefaultDouble) {
->RenderUint32("uint32Value", 0)
->RenderBool("boolValue", false)
->RenderString("stringValue", "")
+ ->RenderString("enumValue", "ENUM_FIRST")
->EndObject();
// Actual testing
@@ -109,6 +122,8 @@ TEST_P(DefaultValueObjectWriterTest, ShouldRetainUnknownField) {
// Set expectation
expects_.StartObject("")
->RenderDouble("doubleValue", 1.0)
+ ->StartList("repeatedDouble")
+ ->EndList()
->RenderFloat("floatValue", 0.0)
->RenderInt64("int64Value", 0)
->RenderUint64("uint64Value", 0)
@@ -120,6 +135,7 @@ TEST_P(DefaultValueObjectWriterTest, ShouldRetainUnknownField) {
->StartObject("unknownObject")
->RenderString("unknown", "def")
->EndObject()
+ ->RenderString("enumValue", "ENUM_FIRST")
->EndObject();
// Actual testing
@@ -132,6 +148,7 @@ TEST_P(DefaultValueObjectWriterTest, ShouldRetainUnknownField) {
->EndObject();
}
+
} // namespace testing
} // namespace converter
} // namespace util
diff --git a/src/google/protobuf/util/internal/error_listener.h b/src/google/protobuf/util/internal/error_listener.h
index 2699684d..3f063936 100644
--- a/src/google/protobuf/util/internal/error_listener.h
+++ b/src/google/protobuf/util/internal/error_listener.h
@@ -31,11 +31,13 @@
#ifndef GOOGLE_PROTOBUF_UTIL_CONVERTER_ERROR_LISTENER_H__
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_ERROR_LISTENER_H__
+#include <algorithm>
#include <memory>
#ifndef _SHARED_PTR_H
#include <google/protobuf/stubs/shared_ptr.h>
#endif
#include <string>
+#include <vector>
#include <google/protobuf/stubs/callback.h>
#include <google/protobuf/stubs/common.h>
diff --git a/src/google/protobuf/util/internal/json_objectwriter_test.cc b/src/google/protobuf/util/internal/json_objectwriter_test.cc
index dcd60601..9d820162 100644
--- a/src/google/protobuf/util/internal/json_objectwriter_test.cc
+++ b/src/google/protobuf/util/internal/json_objectwriter_test.cc
@@ -47,8 +47,7 @@ class JsonObjectWriterTest : public ::testing::Test {
JsonObjectWriterTest()
: str_stream_(new StringOutputStream(&output_)),
out_stream_(new CodedOutputStream(str_stream_)),
- ow_(NULL) {
- }
+ ow_(NULL) {}
virtual ~JsonObjectWriterTest() {
delete ow_;
@@ -64,36 +63,34 @@ class JsonObjectWriterTest : public ::testing::Test {
TEST_F(JsonObjectWriterTest, EmptyRootObject) {
ow_ = new JsonObjectWriter("", out_stream_);
- ow_->StartObject("")
- ->EndObject();
+ ow_->StartObject("")->EndObject();
EXPECT_EQ("{}", output_.substr(0, out_stream_->ByteCount()));
}
TEST_F(JsonObjectWriterTest, EmptyObject) {
ow_ = new JsonObjectWriter("", out_stream_);
ow_->StartObject("")
- ->RenderString("test", "value")
- ->StartObject("empty")
- ->EndObject()
- ->EndObject();
+ ->RenderString("test", "value")
+ ->StartObject("empty")
+ ->EndObject()
+ ->EndObject();
EXPECT_EQ("{\"test\":\"value\",\"empty\":{}}",
output_.substr(0, out_stream_->ByteCount()));
}
TEST_F(JsonObjectWriterTest, EmptyRootList) {
ow_ = new JsonObjectWriter("", out_stream_);
- ow_->StartList("")
- ->EndList();
+ ow_->StartList("")->EndList();
EXPECT_EQ("[]", output_.substr(0, out_stream_->ByteCount()));
}
TEST_F(JsonObjectWriterTest, EmptyList) {
ow_ = new JsonObjectWriter("", out_stream_);
ow_->StartObject("")
- ->RenderString("test", "value")
- ->StartList("empty")
- ->EndList()
- ->EndObject();
+ ->RenderString("test", "value")
+ ->StartList("empty")
+ ->EndList()
+ ->EndObject();
EXPECT_EQ("{\"test\":\"value\",\"empty\":[]}",
output_.substr(0, out_stream_->ByteCount()));
}
@@ -101,10 +98,10 @@ TEST_F(JsonObjectWriterTest, EmptyList) {
TEST_F(JsonObjectWriterTest, ObjectInObject) {
ow_ = new JsonObjectWriter("", out_stream_);
ow_->StartObject("")
- ->StartObject("nested")
- ->RenderString("field", "value")
- ->EndObject()
- ->EndObject();
+ ->StartObject("nested")
+ ->RenderString("field", "value")
+ ->EndObject()
+ ->EndObject();
EXPECT_EQ("{\"nested\":{\"field\":\"value\"}}",
output_.substr(0, out_stream_->ByteCount()));
}
@@ -112,10 +109,10 @@ TEST_F(JsonObjectWriterTest, ObjectInObject) {
TEST_F(JsonObjectWriterTest, ListInObject) {
ow_ = new JsonObjectWriter("", out_stream_);
ow_->StartObject("")
- ->StartList("nested")
- ->RenderString("", "value")
- ->EndList()
- ->EndObject();
+ ->StartList("nested")
+ ->RenderString("", "value")
+ ->EndList()
+ ->EndObject();
EXPECT_EQ("{\"nested\":[\"value\"]}",
output_.substr(0, out_stream_->ByteCount()));
}
@@ -123,10 +120,10 @@ TEST_F(JsonObjectWriterTest, ListInObject) {
TEST_F(JsonObjectWriterTest, ObjectInList) {
ow_ = new JsonObjectWriter("", out_stream_);
ow_->StartList("")
- ->StartObject("")
- ->RenderString("field", "value")
- ->EndObject()
- ->EndList();
+ ->StartObject("")
+ ->RenderString("field", "value")
+ ->EndObject()
+ ->EndList();
EXPECT_EQ("[{\"field\":\"value\"}]",
output_.substr(0, out_stream_->ByteCount()));
}
@@ -134,10 +131,10 @@ TEST_F(JsonObjectWriterTest, ObjectInList) {
TEST_F(JsonObjectWriterTest, ListInList) {
ow_ = new JsonObjectWriter("", out_stream_);
ow_->StartList("")
- ->StartList("")
- ->RenderString("", "value")
- ->EndList()
- ->EndList();
+ ->StartList("")
+ ->RenderString("", "value")
+ ->EndList()
+ ->EndList();
EXPECT_EQ("[[\"value\"]]", output_.substr(0, out_stream_->ByteCount()));
}
@@ -156,14 +153,18 @@ TEST_F(JsonObjectWriterTest, RenderPrimitives) {
->EndObject();
EXPECT_EQ(
"{\"bool\":true,"
- "\"double\":" + ValueAsString<double>(1.7976931348623157e+308) + ","
- "\"float\":" + ValueAsString<float>(3.4028235e+38) + ","
- "\"int\":-2147483648,"
- "\"long\":\"-9223372036854775808\","
- "\"bytes\":\"YWJyYWNhZGFicmE=\","
- "\"string\":\"string\","
- "\"emptybytes\":\"\","
- "\"emptystring\":\"\"}",
+ "\"double\":" +
+ ValueAsString<double>(std::numeric_limits<double>::max()) +
+ ","
+ "\"float\":" +
+ ValueAsString<float>(std::numeric_limits<float>::max()) +
+ ","
+ "\"int\":-2147483648,"
+ "\"long\":\"-9223372036854775808\","
+ "\"bytes\":\"YWJyYWNhZGFicmE=\","
+ "\"string\":\"string\","
+ "\"emptybytes\":\"\","
+ "\"emptystring\":\"\"}",
output_.substr(0, out_stream_->ByteCount()));
}
@@ -181,81 +182,83 @@ TEST_F(JsonObjectWriterTest, BytesEncodesAsNonWebSafeBase64) {
TEST_F(JsonObjectWriterTest, PrettyPrintList) {
ow_ = new JsonObjectWriter(" ", out_stream_);
ow_->StartObject("")
- ->StartList("items")
- ->RenderString("", "item1")
- ->RenderString("", "item2")
- ->RenderString("", "item3")
- ->EndList()
- ->StartList("empty")
- ->EndList()
- ->EndObject();
- EXPECT_EQ("{\n"
- " \"items\": [\n"
- " \"item1\",\n"
- " \"item2\",\n"
- " \"item3\"\n"
- " ],\n"
- " \"empty\": []\n"
- "}\n",
- output_.substr(0, out_stream_->ByteCount()));
+ ->StartList("items")
+ ->RenderString("", "item1")
+ ->RenderString("", "item2")
+ ->RenderString("", "item3")
+ ->EndList()
+ ->StartList("empty")
+ ->EndList()
+ ->EndObject();
+ EXPECT_EQ(
+ "{\n"
+ " \"items\": [\n"
+ " \"item1\",\n"
+ " \"item2\",\n"
+ " \"item3\"\n"
+ " ],\n"
+ " \"empty\": []\n"
+ "}\n",
+ output_.substr(0, out_stream_->ByteCount()));
}
TEST_F(JsonObjectWriterTest, PrettyPrintObject) {
ow_ = new JsonObjectWriter(" ", out_stream_);
ow_->StartObject("")
- ->StartObject("items")
- ->RenderString("key1", "item1")
- ->RenderString("key2", "item2")
- ->RenderString("key3", "item3")
- ->EndObject()
- ->StartObject("empty")
- ->EndObject()
- ->EndObject();
- EXPECT_EQ("{\n"
- " \"items\": {\n"
- " \"key1\": \"item1\",\n"
- " \"key2\": \"item2\",\n"
- " \"key3\": \"item3\"\n"
- " },\n"
- " \"empty\": {}\n"
- "}\n",
- output_.substr(0, out_stream_->ByteCount()));
+ ->StartObject("items")
+ ->RenderString("key1", "item1")
+ ->RenderString("key2", "item2")
+ ->RenderString("key3", "item3")
+ ->EndObject()
+ ->StartObject("empty")
+ ->EndObject()
+ ->EndObject();
+ EXPECT_EQ(
+ "{\n"
+ " \"items\": {\n"
+ " \"key1\": \"item1\",\n"
+ " \"key2\": \"item2\",\n"
+ " \"key3\": \"item3\"\n"
+ " },\n"
+ " \"empty\": {}\n"
+ "}\n",
+ output_.substr(0, out_stream_->ByteCount()));
}
TEST_F(JsonObjectWriterTest, PrettyPrintEmptyObjectInEmptyList) {
ow_ = new JsonObjectWriter(" ", out_stream_);
ow_->StartObject("")
- ->StartList("list")
- ->StartObject("")
- ->EndObject()
- ->EndList()
- ->EndObject();
- EXPECT_EQ("{\n"
- " \"list\": [\n"
- " {}\n"
- " ]\n"
- "}\n",
- output_.substr(0, out_stream_->ByteCount()));
+ ->StartList("list")
+ ->StartObject("")
+ ->EndObject()
+ ->EndList()
+ ->EndObject();
+ EXPECT_EQ(
+ "{\n"
+ " \"list\": [\n"
+ " {}\n"
+ " ]\n"
+ "}\n",
+ output_.substr(0, out_stream_->ByteCount()));
}
TEST_F(JsonObjectWriterTest, PrettyPrintDoubleIndent) {
ow_ = new JsonObjectWriter(" ", out_stream_);
ow_->StartObject("")
- ->RenderBool("bool", true)
- ->RenderInt32("int", 42)
- ->EndObject();
- EXPECT_EQ("{\n"
- " \"bool\": true,\n"
- " \"int\": 42\n"
- "}\n",
- output_.substr(0, out_stream_->ByteCount()));
+ ->RenderBool("bool", true)
+ ->RenderInt32("int", 42)
+ ->EndObject();
+ EXPECT_EQ(
+ "{\n"
+ " \"bool\": true,\n"
+ " \"int\": 42\n"
+ "}\n",
+ output_.substr(0, out_stream_->ByteCount()));
}
TEST_F(JsonObjectWriterTest, StringsEscapedAndEnclosedInDoubleQuotes) {
ow_ = new JsonObjectWriter("", out_stream_);
- ow_->StartObject("")
- ->RenderString("string", "'<>&amp;\\\"\r\n")
- ->EndObject();
+ ow_->StartObject("")->RenderString("string", "'<>&amp;\\\"\r\n")->EndObject();
EXPECT_EQ("{\"string\":\"'\\u003c\\u003e&amp;\\\\\\\"\\r\\n\"}",
output_.substr(0, out_stream_->ByteCount()));
}
@@ -263,13 +266,13 @@ TEST_F(JsonObjectWriterTest, StringsEscapedAndEnclosedInDoubleQuotes) {
TEST_F(JsonObjectWriterTest, Stringification) {
ow_ = new JsonObjectWriter("", out_stream_);
ow_->StartObject("")
- ->RenderDouble("double_nan", std::numeric_limits<double>::quiet_NaN())
- ->RenderFloat("float_nan", std::numeric_limits<float>::quiet_NaN())
- ->RenderDouble("double_pos", std::numeric_limits<double>::infinity())
- ->RenderFloat("float_pos", std::numeric_limits<float>::infinity())
- ->RenderDouble("double_neg", -std::numeric_limits<double>::infinity())
- ->RenderFloat("float_neg", -std::numeric_limits<float>::infinity())
- ->EndObject();
+ ->RenderDouble("double_nan", std::numeric_limits<double>::quiet_NaN())
+ ->RenderFloat("float_nan", std::numeric_limits<float>::quiet_NaN())
+ ->RenderDouble("double_pos", std::numeric_limits<double>::infinity())
+ ->RenderFloat("float_pos", std::numeric_limits<float>::infinity())
+ ->RenderDouble("double_neg", -std::numeric_limits<double>::infinity())
+ ->RenderFloat("float_neg", -std::numeric_limits<float>::infinity())
+ ->EndObject();
EXPECT_EQ(
"{\"double_nan\":\"NaN\","
"\"float_nan\":\"NaN\","
diff --git a/src/google/protobuf/util/internal/json_stream_parser.cc b/src/google/protobuf/util/internal/json_stream_parser.cc
index a7ef7fe2..df916751 100644
--- a/src/google/protobuf/util/internal/json_stream_parser.cc
+++ b/src/google/protobuf/util/internal/json_stream_parser.cc
@@ -157,10 +157,10 @@ util::Status JsonStreamParser::FinishParse() {
char* coerced = internal::UTF8CoerceToStructurallyValid(leftover_, utf8.get(), ' ');
p_ = json_ = StringPiece(coerced, leftover_.size());
} else {
+ p_ = json_ = leftover_;
if (!internal::IsStructurallyValidUTF8(leftover_)) {
return ReportFailure("Encountered non UTF-8 code points.");
}
- p_ = json_ = leftover_;
}
// Parse the remainder in finishing mode, which reports errors for things like
diff --git a/src/google/protobuf/util/internal/json_stream_parser_test.cc b/src/google/protobuf/util/internal/json_stream_parser_test.cc
index c833ed1f..3414826e 100644
--- a/src/google/protobuf/util/internal/json_stream_parser_test.cc
+++ b/src/google/protobuf/util/internal/json_stream_parser_test.cc
@@ -348,6 +348,7 @@ TEST_F(JsonStreamParserTest, RejectNonUtf8WhenNotCoerced) {
for (int i = 0; i <= json.length(); ++i) {
DoErrorTest(json, i, "Encountered non UTF-8 code points.");
}
+ DoErrorTest("\xFF{}", 0, "Encountered non UTF-8 code points.");
}
#ifndef _MSC_VER
diff --git a/src/google/protobuf/util/internal/object_writer.h b/src/google/protobuf/util/internal/object_writer.h
index 20bd3627..e695f45e 100644
--- a/src/google/protobuf/util/internal/object_writer.h
+++ b/src/google/protobuf/util/internal/object_writer.h
@@ -101,11 +101,6 @@ class LIBPROTOBUF_EXPORT ObjectWriter {
// Renders a Null value.
virtual ObjectWriter* RenderNull(StringPiece name) = 0;
- // Disables case normalization. Any RenderTYPE call after calling this
- // function will output the name field as-is. No normalization is attempted on
- // it. This setting is reset immediately after the next RenderTYPE is called.
- virtual ObjectWriter* DisableCaseNormalizationForNextKey() { return this; }
-
// 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
new file mode 100644
index 00000000..47e0009e
--- /dev/null
+++ b/src/google/protobuf/util/internal/proto_writer.cc
@@ -0,0 +1,744 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <google/protobuf/util/internal/proto_writer.h>
+
+#include <functional>
+#include <stack>
+
+#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/stubs/time.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/util/internal/field_mask_utility.h>
+#include <google/protobuf/util/internal/object_location_tracker.h>
+#include <google/protobuf/util/internal/constants.h>
+#include <google/protobuf/util/internal/utility.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/map_util.h>
+#include <google/protobuf/stubs/statusor.h>
+
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+using google::protobuf::internal::WireFormatLite;
+using google::protobuf::io::CodedOutputStream;
+using util::error::INVALID_ARGUMENT;
+using util::Status;
+using util::StatusOr;
+
+
+ProtoWriter::ProtoWriter(TypeResolver* type_resolver,
+ const google::protobuf::Type& type,
+ strings::ByteSink* output, ErrorListener* listener)
+ : master_type_(type),
+ typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
+ own_typeinfo_(true),
+ done_(false),
+ element_(NULL),
+ size_insert_(),
+ output_(output),
+ buffer_(),
+ adapter_(&buffer_),
+ stream_(new CodedOutputStream(&adapter_)),
+ listener_(listener),
+ invalid_depth_(0),
+ tracker_(new ObjectLocationTracker()) {}
+
+ProtoWriter::ProtoWriter(const TypeInfo* typeinfo,
+ const google::protobuf::Type& type,
+ strings::ByteSink* output, ErrorListener* listener)
+ : master_type_(type),
+ typeinfo_(typeinfo),
+ own_typeinfo_(false),
+ done_(false),
+ element_(NULL),
+ size_insert_(),
+ output_(output),
+ buffer_(),
+ adapter_(&buffer_),
+ stream_(new CodedOutputStream(&adapter_)),
+ listener_(listener),
+ invalid_depth_(0),
+ tracker_(new ObjectLocationTracker()) {}
+
+ProtoWriter::~ProtoWriter() {
+ if (own_typeinfo_) {
+ delete typeinfo_;
+ }
+ if (element_ == NULL) 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(
+ static_cast<BaseElement*>(element_.get())->pop<BaseElement>());
+ while (element != NULL) {
+ element.reset(element->pop<BaseElement>());
+ }
+}
+
+namespace {
+
+// Writes an INT32 field, including tag to the stream.
+inline Status WriteInt32(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<int32> i32 = data.ToInt32();
+ if (i32.ok()) {
+ WireFormatLite::WriteInt32(field_number, i32.ValueOrDie(), stream);
+ }
+ return i32.status();
+}
+
+// writes an SFIXED32 field, including tag, to the stream.
+inline Status WriteSFixed32(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<int32> i32 = data.ToInt32();
+ if (i32.ok()) {
+ WireFormatLite::WriteSFixed32(field_number, i32.ValueOrDie(), stream);
+ }
+ return i32.status();
+}
+
+// Writes an SINT32 field, including tag, to the stream.
+inline Status WriteSInt32(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<int32> i32 = data.ToInt32();
+ if (i32.ok()) {
+ WireFormatLite::WriteSInt32(field_number, i32.ValueOrDie(), stream);
+ }
+ return i32.status();
+}
+
+// Writes a FIXED32 field, including tag, to the stream.
+inline Status WriteFixed32(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<uint32> u32 = data.ToUint32();
+ if (u32.ok()) {
+ WireFormatLite::WriteFixed32(field_number, u32.ValueOrDie(), stream);
+ }
+ return u32.status();
+}
+
+// Writes a UINT32 field, including tag, to the stream.
+inline Status WriteUInt32(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<uint32> u32 = data.ToUint32();
+ if (u32.ok()) {
+ WireFormatLite::WriteUInt32(field_number, u32.ValueOrDie(), stream);
+ }
+ return u32.status();
+}
+
+// Writes an INT64 field, including tag, to the stream.
+inline Status WriteInt64(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<int64> i64 = data.ToInt64();
+ if (i64.ok()) {
+ WireFormatLite::WriteInt64(field_number, i64.ValueOrDie(), stream);
+ }
+ return i64.status();
+}
+
+// Writes an SFIXED64 field, including tag, to the stream.
+inline Status WriteSFixed64(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<int64> i64 = data.ToInt64();
+ if (i64.ok()) {
+ WireFormatLite::WriteSFixed64(field_number, i64.ValueOrDie(), stream);
+ }
+ return i64.status();
+}
+
+// Writes an SINT64 field, including tag, to the stream.
+inline Status WriteSInt64(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<int64> i64 = data.ToInt64();
+ if (i64.ok()) {
+ WireFormatLite::WriteSInt64(field_number, i64.ValueOrDie(), stream);
+ }
+ return i64.status();
+}
+
+// Writes a FIXED64 field, including tag, to the stream.
+inline Status WriteFixed64(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<uint64> u64 = data.ToUint64();
+ if (u64.ok()) {
+ WireFormatLite::WriteFixed64(field_number, u64.ValueOrDie(), stream);
+ }
+ return u64.status();
+}
+
+// Writes a UINT64 field, including tag, to the stream.
+inline Status WriteUInt64(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<uint64> u64 = data.ToUint64();
+ if (u64.ok()) {
+ WireFormatLite::WriteUInt64(field_number, u64.ValueOrDie(), stream);
+ }
+ return u64.status();
+}
+
+// Writes a DOUBLE field, including tag, to the stream.
+inline Status WriteDouble(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<double> d = data.ToDouble();
+ if (d.ok()) {
+ WireFormatLite::WriteDouble(field_number, d.ValueOrDie(), stream);
+ }
+ return d.status();
+}
+
+// Writes a FLOAT field, including tag, to the stream.
+inline Status WriteFloat(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<float> f = data.ToFloat();
+ if (f.ok()) {
+ WireFormatLite::WriteFloat(field_number, f.ValueOrDie(), stream);
+ }
+ return f.status();
+}
+
+// Writes a BOOL field, including tag, to the stream.
+inline Status WriteBool(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<bool> b = data.ToBool();
+ if (b.ok()) {
+ WireFormatLite::WriteBool(field_number, b.ValueOrDie(), stream);
+ }
+ return b.status();
+}
+
+// Writes a BYTES field, including tag, to the stream.
+inline Status WriteBytes(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<string> c = data.ToBytes();
+ if (c.ok()) {
+ WireFormatLite::WriteBytes(field_number, c.ValueOrDie(), stream);
+ }
+ return c.status();
+}
+
+// Writes a STRING field, including tag, to the stream.
+inline Status WriteString(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<string> s = data.ToString();
+ if (s.ok()) {
+ WireFormatLite::WriteString(field_number, s.ValueOrDie(), stream);
+ }
+ return s.status();
+}
+
+// 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);
+ if (e.ok()) {
+ WireFormatLite::WriteEnum(field_number, e.ValueOrDie(), stream);
+ }
+ return e.status();
+}
+
+// Given a google::protobuf::Type, returns the set of all required fields.
+std::set<const google::protobuf::Field*> GetRequiredFields(
+ const google::protobuf::Type& type) {
+ std::set<const google::protobuf::Field*> required;
+ for (int i = 0; i < type.fields_size(); i++) {
+ const google::protobuf::Field& field = type.fields(i);
+ if (field.cardinality() ==
+ google::protobuf::Field_Cardinality_CARDINALITY_REQUIRED) {
+ required.insert(&field);
+ }
+ }
+ return required;
+}
+
+} // namespace
+
+ProtoWriter::ProtoElement::ProtoElement(const TypeInfo* typeinfo,
+ const google::protobuf::Type& type,
+ ProtoWriter* enclosing)
+ : BaseElement(NULL),
+ ow_(enclosing),
+ parent_field_(NULL),
+ typeinfo_(typeinfo),
+ type_(type),
+ required_fields_(GetRequiredFields(type)),
+ size_index_(-1),
+ array_index_(-1) {}
+
+ProtoWriter::ProtoElement::ProtoElement(ProtoWriter::ProtoElement* parent,
+ const google::protobuf::Field* field,
+ const google::protobuf::Type& type,
+ bool is_list)
+ : BaseElement(parent),
+ ow_(this->parent()->ow_),
+ parent_field_(field),
+ typeinfo_(this->parent()->typeinfo_),
+ 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) {
+ 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 {
+ this->parent()->RegisterField(field);
+ }
+
+ if (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) {
+ 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
+ // of any nested messages. We start with -start_pos here, so we only
+ // need to add the final buffer position to it at the end.
+ SizeInfo info = {start_pos, -start_pos};
+ ow_->size_insert_.push_back(info);
+ }
+ }
+}
+
+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());
+ }
+ // 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.
+ // If size_index_ < 0, this is not a message, so no size field is added.
+ if (size_index_ >= 0) {
+ // Add the final buffer position to compute the total length of this
+ // serialized message. The stored value (before this addition) already
+ // contains the total length of the size fields of all nested messages
+ // minus the initial buffer position.
+ ow_->size_insert_[size_index_].size += ow_->stream_->ByteCount();
+ // Calculate the length required to serialize the size field of the
+ // message, and propagate this additional size information upward to
+ // 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()) {
+ // 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;
+ }
+ }
+ }
+ return BaseElement::pop<ProtoElement>();
+}
+
+void ProtoWriter::ProtoElement::RegisterField(
+ const google::protobuf::Field* field) {
+ if (!required_fields_.empty() &&
+ field->cardinality() ==
+ google::protobuf::Field_Cardinality_CARDINALITY_REQUIRED) {
+ required_fields_.erase(field);
+ }
+}
+
+string ProtoWriter::ProtoElement::ToString() const {
+ if (parent() == NULL) return "";
+ string loc = parent()->ToString();
+ if (!ow_->IsRepeated(*parent_field_) ||
+ parent()->parent_field_ != parent_field_) {
+ string name = parent_field_->name();
+ int i = 0;
+ while (i < name.size() && (ascii_isalnum(name[i]) || name[i] == '_')) ++i;
+ if (i > 0 && i == name.size()) { // safe field name
+ if (loc.empty()) {
+ loc = name;
+ } else {
+ StrAppend(&loc, ".", name);
+ }
+ } else {
+ StrAppend(&loc, "[\"", CEscape(name), "\"]");
+ }
+ }
+ if (ow_->IsRepeated(*parent_field_) && array_index_ > 0) {
+ StrAppend(&loc, "[", array_index_ - 1, "]");
+ }
+ return loc.empty() ? "." : loc;
+}
+
+bool ProtoWriter::ProtoElement::IsOneofIndexTaken(int32 index) {
+ return ContainsKey(oneof_indices_, index);
+}
+
+void ProtoWriter::ProtoElement::TakeOneofIndex(int32 index) {
+ InsertIfNotPresent(&oneof_indices_, index);
+}
+
+void ProtoWriter::InvalidName(StringPiece unknown_name, StringPiece message) {
+ listener_->InvalidName(location(), ToSnakeCase(unknown_name), message);
+}
+
+void ProtoWriter::InvalidValue(StringPiece type_name, StringPiece value) {
+ listener_->InvalidValue(location(), type_name, value);
+}
+
+void ProtoWriter::MissingField(StringPiece missing_name) {
+ listener_->MissingField(location(), missing_name);
+}
+
+ProtoWriter* ProtoWriter::StartObject(StringPiece name) {
+ // Starting the root message. Create the root ProtoElement and return.
+ if (element_ == NULL) {
+ if (!name.empty()) {
+ InvalidName(name, "Root element should not be named.");
+ }
+ element_.reset(new ProtoElement(typeinfo_, master_type_, this));
+ return this;
+ }
+
+ const google::protobuf::Field* field = NULL;
+ field = BeginNamed(name, false);
+ if (field == NULL) return this;
+
+ // Check to see if this field is a oneof and that no oneof in that group has
+ // already been set.
+ if (!ValidOneof(*field, name)) {
+ ++invalid_depth_;
+ return this;
+ }
+
+ const google::protobuf::Type* type = LookupType(field);
+ if (type == NULL) {
+ ++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;
+}
+
+ProtoWriter* ProtoWriter::EndObject() {
+ if (invalid_depth_ > 0) {
+ --invalid_depth_;
+ return this;
+ }
+
+ if (element_ != NULL) {
+ element_.reset(element_->pop());
+ }
+
+
+ // If ending the root element,
+ // then serialize the full message with calculated sizes.
+ if (element_ == NULL) {
+ WriteRootMessage();
+ }
+ return this;
+}
+
+ProtoWriter* ProtoWriter::StartList(StringPiece name) {
+ const google::protobuf::Field* field = BeginNamed(name, true);
+ if (field == NULL) return this;
+
+ if (!ValidOneof(*field, name)) {
+ ++invalid_depth_;
+ return this;
+ }
+
+ const google::protobuf::Type* type = LookupType(field);
+ if (type == NULL) {
+ ++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;
+}
+
+ProtoWriter* ProtoWriter::EndList() {
+ if (invalid_depth_ > 0) {
+ --invalid_depth_;
+ } else if (element_ != NULL) {
+ element_.reset(element_->pop());
+ }
+ return this;
+}
+
+ProtoWriter* ProtoWriter::RenderDataPiece(StringPiece name,
+ const DataPiece& data) {
+ Status status;
+ if (invalid_depth_ > 0) return this;
+
+ const google::protobuf::Field* field = Lookup(name);
+ if (field == NULL) return this;
+
+ if (!ValidOneof(*field, name)) return this;
+
+ const google::protobuf::Type* type = LookupType(field);
+ if (type == NULL) {
+ InvalidName(name,
+ StrCat("Missing descriptor for field: ", field->type_url()));
+ return this;
+ }
+
+ // 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(),
+ data.ValueAsStringOrDefault(""));
+ element_.reset(element()->pop());
+ return this;
+ }
+
+ switch (field->kind()) {
+ case google::protobuf::Field_Kind_TYPE_INT32: {
+ status = WriteInt32(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SFIXED32: {
+ status = WriteSFixed32(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SINT32: {
+ status = WriteSInt32(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_FIXED32: {
+ status = WriteFixed32(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_UINT32: {
+ status = WriteUInt32(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_INT64: {
+ status = WriteInt64(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SFIXED64: {
+ status = WriteSFixed64(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SINT64: {
+ status = WriteSInt64(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_FIXED64: {
+ status = WriteFixed64(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_UINT64: {
+ status = WriteUInt64(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_DOUBLE: {
+ status = WriteDouble(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_FLOAT: {
+ status = WriteFloat(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_BOOL: {
+ status = WriteBool(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_BYTES: {
+ status = WriteBytes(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_STRING: {
+ 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());
+ break;
+ }
+ default: // TYPE_GROUP or TYPE_MESSAGE
+ status = Status(INVALID_ARGUMENT, data.ToString().ValueOrDie());
+ }
+
+ if (!status.ok()) {
+ InvalidValue(google::protobuf::Field_Kind_Name(field->kind()),
+ status.error_message());
+ }
+
+ 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;
+}
+
+bool ProtoWriter::IsRepeated(const google::protobuf::Field& field) {
+ return field.cardinality() ==
+ google::protobuf::Field_Cardinality_CARDINALITY_REPEATED;
+}
+
+const google::protobuf::Field* ProtoWriter::BeginNamed(StringPiece name,
+ bool is_list) {
+ if (invalid_depth_ > 0) {
+ ++invalid_depth_;
+ return NULL;
+ }
+ const google::protobuf::Field* field = Lookup(name);
+ if (field == NULL) {
+ ++invalid_depth_;
+ // InvalidName() already called in Lookup().
+ return NULL;
+ }
+ if (is_list && !IsRepeated(*field)) {
+ ++invalid_depth_;
+ InvalidName(name, "Proto field is not repeating, cannot start list.");
+ return NULL;
+ }
+ return field;
+}
+
+const google::protobuf::Field* ProtoWriter::Lookup(
+ StringPiece unnormalized_name) {
+ ProtoElement* e = element();
+ if (e == NULL) {
+ InvalidName(unnormalized_name, "Root element must be a message.");
+ return NULL;
+ }
+ if (unnormalized_name.empty()) {
+ // Objects in repeated field inherit the same field descriptor.
+ if (e->parent_field() == NULL) {
+ 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 e->parent_field();
+ }
+ const google::protobuf::Field* field =
+ typeinfo_->FindField(&e->type(), unnormalized_name);
+ if (field == NULL) InvalidName(unnormalized_name, "Cannot find field.");
+ return field;
+}
+
+const google::protobuf::Type* ProtoWriter::LookupType(
+ const google::protobuf::Field* field) {
+ return ((field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE ||
+ field->kind() == google::protobuf::Field_Kind_TYPE_GROUP)
+ ? typeinfo_->GetTypeByTypeUrl(field->type_url())
+ : &element_->type());
+}
+
+void ProtoWriter::WriteRootMessage() {
+ GOOGLE_DCHECK(!done_);
+ int curr_pos = 0;
+ // Calls the destructor of CodedOutputStream to remove any uninitialized
+ // memory from the Cord before we read it.
+ stream_.reset(NULL);
+ const void* data;
+ int length;
+ google::protobuf::io::ArrayInputStream input_stream(buffer_.data(), buffer_.size());
+ while (input_stream.Next(&data, &length)) {
+ if (length == 0) continue;
+ int num_bytes = length;
+ // Write up to where we need to insert the size field.
+ // The number of bytes we may write is the smaller of:
+ // - the current fragment size
+ // - the distance to the next position where a size field needs to be
+ // inserted.
+ if (!size_insert_.empty() &&
+ size_insert_.front().pos - curr_pos < num_bytes) {
+ num_bytes = size_insert_.front().pos - curr_pos;
+ }
+ output_->Append(static_cast<const char*>(data), num_bytes);
+ if (num_bytes < length) {
+ input_stream.BackUp(length - num_bytes);
+ }
+ curr_pos += num_bytes;
+ // Insert the size field.
+ // size_insert_.front(): the next <index, size> pair to be written.
+ // size_insert_.front().pos: position of the size field.
+ // size_insert_.front().size: the size (integer) to be inserted.
+ if (!size_insert_.empty() && curr_pos == size_insert_.front().pos) {
+ // Varint32 occupies at most 10 bytes.
+ uint8 insert_buffer[10];
+ uint8* insert_buffer_pos = CodedOutputStream::WriteVarint32ToArray(
+ size_insert_.front().size, insert_buffer);
+ output_->Append(reinterpret_cast<const char*>(insert_buffer),
+ insert_buffer_pos - insert_buffer);
+ size_insert_.pop_front();
+ }
+ }
+ output_->Flush();
+ stream_.reset(new CodedOutputStream(&adapter_));
+ done_ = true;
+}
+
+void ProtoWriter::WriteTag(const google::protobuf::Field& field) {
+ WireFormatLite::WireType wire_type = WireFormatLite::WireTypeForFieldType(
+ static_cast<WireFormatLite::FieldType>(field.kind()));
+ stream_->WriteTag(WireFormatLite::MakeTag(field.number(), wire_type));
+}
+
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/proto_writer.h b/src/google/protobuf/util/internal/proto_writer.h
new file mode 100644
index 00000000..e631e56f
--- /dev/null
+++ b/src/google/protobuf/util/internal/proto_writer.h
@@ -0,0 +1,315 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTO_WRITER_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTO_WRITER_H__
+
+#include <deque>
+#include <google/protobuf/stubs/hash.h>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/util/internal/type_info.h>
+#include <google/protobuf/util/internal/datapiece.h>
+#include <google/protobuf/util/internal/error_listener.h>
+#include <google/protobuf/util/internal/structured_objectwriter.h>
+#include <google/protobuf/util/type_resolver.h>
+#include <google/protobuf/stubs/bytestream.h>
+
+namespace google {
+namespace protobuf {
+namespace io {
+class CodedOutputStream;
+} // namespace io
+} // namespace protobuf
+
+
+namespace protobuf {
+class Type;
+class Field;
+} // namespace protobuf
+
+
+namespace protobuf {
+namespace util {
+namespace converter {
+
+class ObjectLocationTracker;
+
+// An ObjectWriter that can write protobuf bytes directly from writer events.
+// This class does not support special types like Struct or Map. However, since
+// this class supports raw protobuf, it can be used to provide support for
+// special types by inheriting from it or by wrapping it.
+//
+// It also supports streaming.
+class LIBPROTOBUF_EXPORT ProtoWriter : public StructuredObjectWriter {
+ public:
+// Constructor. Does not take ownership of any parameter passed in.
+ ProtoWriter(TypeResolver* type_resolver, const google::protobuf::Type& type,
+ strings::ByteSink* output, ErrorListener* listener);
+ virtual ~ProtoWriter();
+
+ // ObjectWriter methods.
+ virtual ProtoWriter* StartObject(StringPiece name);
+ virtual ProtoWriter* EndObject();
+ virtual ProtoWriter* StartList(StringPiece name);
+ virtual ProtoWriter* EndList();
+ virtual ProtoWriter* RenderBool(StringPiece name, bool value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoWriter* RenderInt32(StringPiece name, int32 value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoWriter* RenderUint32(StringPiece name, uint32 value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoWriter* RenderInt64(StringPiece name, int64 value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoWriter* RenderUint64(StringPiece name, uint64 value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoWriter* RenderDouble(StringPiece name, double value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoWriter* RenderFloat(StringPiece name, float value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoWriter* RenderString(StringPiece name, StringPiece value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoWriter* RenderBytes(StringPiece name, StringPiece value) {
+ return RenderDataPiece(name, DataPiece(value, false));
+ }
+ virtual ProtoWriter* RenderNull(StringPiece name) {
+ 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,
+ const DataPiece& value);
+
+ // Returns the location tracker to use for tracking locations for errors.
+ const LocationTrackerInterface& location() {
+ return element_ != NULL ? *element_ : *tracker_;
+ }
+
+ // When true, we finished writing to output a complete message.
+ bool done() const { return done_; }
+
+ // Returns the proto stream object.
+ google::protobuf::io::CodedOutputStream* stream() { return stream_.get(); }
+
+ // Getters and mutators of invalid_depth_.
+ void IncrementInvalidDepth() { ++invalid_depth_; }
+ void DecrementInvalidDepth() { --invalid_depth_; }
+ int invalid_depth() { return invalid_depth_; }
+
+ ErrorListener* listener() { return listener_; }
+
+ const TypeInfo* typeinfo() { return typeinfo_; }
+
+ protected:
+ class LIBPROTOBUF_EXPORT ProtoElement : public BaseElement, public LocationTrackerInterface {
+ public:
+ // Constructor for the root element. No parent nor field.
+ ProtoElement(const TypeInfo* typeinfo, const google::protobuf::Type& type,
+ ProtoWriter* enclosing);
+
+ // Constructor for a field of an element.
+ ProtoElement(ProtoElement* parent, const google::protobuf::Field* field,
+ const google::protobuf::Type& type, bool is_list);
+
+ virtual ~ProtoElement() {}
+
+ // Called just before the destructor for clean up:
+ // - reports any missing required fields
+ // - computes the space needed by the size field, and augment the
+ // length of all parent messages by this additional space.
+ // - releases and returns the parent pointer.
+ ProtoElement* pop();
+
+ // Accessors
+ // parent_field() may be NULL if we are at root.
+ const google::protobuf::Field* parent_field() const {
+ return parent_field_;
+ }
+ const google::protobuf::Type& type() const { return type_; }
+
+ // Registers field for accounting required fields.
+ void RegisterField(const google::protobuf::Field* field);
+
+ // To report location on error messages.
+ virtual string ToString() const;
+
+ virtual ProtoElement* parent() const {
+ return static_cast<ProtoElement*>(BaseElement::parent());
+ }
+
+ // Returns true if the index is already taken by a preceeding oneof input.
+ bool IsOneofIndexTaken(int32 index);
+
+ // Marks the oneof 'index' as taken. Future inputs to this oneof will
+ // generate an error.
+ void TakeOneofIndex(int32 index);
+
+ private:
+ // Used for access to variables of the enclosing instance of
+ // ProtoWriter.
+ ProtoWriter* ow_;
+
+ // Describes the element as a field in the parent message.
+ // parent_field_ is NULL if and only if this element is the root element.
+ const google::protobuf::Field* parent_field_;
+
+ // TypeInfo to lookup types.
+ const TypeInfo* typeinfo_;
+
+ // Additional variables if this element is a message:
+ // (Root element is always a message).
+ // type_ : the type of this element.
+ // required_fields_ : set of required fields.
+ // size_index_ : index into ProtoWriter::size_insert_
+ // for later insertion of serialized message length.
+ const google::protobuf::Type& type_;
+ std::set<const google::protobuf::Field*> required_fields_;
+ const int size_index_;
+
+ // Tracks position in repeated fields, needed for LocationTrackerInterface.
+ int array_index_;
+
+ // Set of oneof indices already seen for the type_. Used to validate
+ // incoming messages so no more than one oneof is set.
+ hash_set<int32> oneof_indices_;
+
+ GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoElement);
+ };
+
+ // Container for inserting 'size' information at the 'pos' position.
+ struct SizeInfo {
+ const int pos;
+ int size;
+ };
+
+ ProtoWriter(const TypeInfo* typeinfo, const google::protobuf::Type& type,
+ strings::ByteSink* output, ErrorListener* listener);
+
+ virtual ProtoElement* element() { return element_.get(); }
+
+ // Helper methods for calling ErrorListener. See error_listener.h.
+ void InvalidName(StringPiece unknown_name, StringPiece message);
+ void InvalidValue(StringPiece type_name, StringPiece value);
+ void MissingField(StringPiece missing_name);
+
+ // Common code for BeginObject() and BeginList() that does invalid_depth_
+ // bookkeeping associated with name lookup.
+ const google::protobuf::Field* BeginNamed(StringPiece name, bool is_list);
+
+ // Lookup the field in the current element. Looks in the base descriptor
+ // and in any extension. This will report an error if the field cannot be
+ // found or if multiple matching extensions are found.
+ const google::protobuf::Field* Lookup(StringPiece name);
+
+ // Lookup the field type in the type descriptor. Returns NULL if the type
+ // is not known.
+ const google::protobuf::Type* LookupType(
+ const google::protobuf::Field* field);
+
+ // Write serialized output to the final output ByteSink, inserting all
+ // the size information for nested messages that are missing from the
+ // intermediate Cord buffer.
+ void WriteRootMessage();
+
+ // Helper method to write proto tags based on the given field.
+ void WriteTag(const google::protobuf::Field& field);
+
+
+ // Returns true if the field for type_ can be set as a oneof. If field is not
+ // a oneof type, this function does nothing and returns true.
+ // If another field for this oneof is already set, this function returns
+ // false. It also calls the appropriate error callback.
+ // unnormalized_name is used for error string.
+ bool ValidOneof(const google::protobuf::Field& field,
+ StringPiece unnormalized_name);
+
+ // Returns true if the field is repeated.
+ bool IsRepeated(const google::protobuf::Field& field);
+
+ private:
+ // Variables for describing the structure of the input tree:
+ // master_type_: descriptor for the whole protobuf message.
+ // typeinfo_ : the TypeInfo object to lookup types.
+ const google::protobuf::Type& master_type_;
+ const TypeInfo* typeinfo_;
+ // Whether we own the typeinfo_ object.
+ bool own_typeinfo_;
+
+ // Indicates whether we finished writing root message completely.
+ bool done_;
+
+ // Variable for internal state processing:
+ // element_ : the current element.
+ // size_insert_: sizes of nested messages.
+ // pos - position to insert the size field.
+ // size - size value to be inserted.
+ google::protobuf::scoped_ptr<ProtoElement> element_;
+ std::deque<SizeInfo> size_insert_;
+
+ // Variables for output generation:
+ // output_ : pointer to an external ByteSink for final user-visible output.
+ // buffer_ : buffer holding partial message before being ready for output_.
+ // adapter_ : internal adapter between CodedOutputStream and buffer_.
+ // stream_ : wrapper for writing tags and other encodings in wire format.
+ strings::ByteSink* output_;
+ string buffer_;
+ google::protobuf::io::StringOutputStream adapter_;
+ google::protobuf::scoped_ptr<google::protobuf::io::CodedOutputStream> stream_;
+
+ // Variables for error tracking and reporting:
+ // listener_ : a place to report any errors found.
+ // invalid_depth_: number of enclosing invalid nested messages.
+ // tracker_ : the root location tracker interface.
+ ErrorListener* listener_;
+ int invalid_depth_;
+ google::protobuf::scoped_ptr<LocationTrackerInterface> tracker_;
+
+ GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoWriter);
+};
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTO_WRITER_H__
diff --git a/src/google/protobuf/util/internal/protostream_objectsource.cc b/src/google/protobuf/util/internal/protostream_objectsource.cc
index 2bbb1597..034d616f 100644
--- a/src/google/protobuf/util/internal/protostream_objectsource.cc
+++ b/src/google/protobuf/util/internal/protostream_objectsource.cc
@@ -140,10 +140,10 @@ Status ProtoStreamObjectSource::WriteMessage(const google::protobuf::Type& type,
bool include_start_and_end,
ObjectWriter* ow) const {
- const TypeRenderer* type_renderer = FindTypeRenderer(type.name());
- if (type_renderer != NULL) {
- return (*type_renderer)(this, type, name, ow);
- }
+ const TypeRenderer* type_renderer = FindTypeRenderer(type.name());
+ if (type_renderer != NULL) {
+ return (*type_renderer)(this, type, name, ow);
+ }
const google::protobuf::Field* field = NULL;
string field_name;
@@ -171,7 +171,9 @@ Status ProtoStreamObjectSource::WriteMessage(const google::protobuf::Type& type,
if (field->cardinality() ==
google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
- if (IsMap(*field)) {
+ bool check_maps = true;
+
+ if (check_maps && IsMap(*field)) {
ow->StartObject(field_name);
ASSIGN_OR_RETURN(tag, RenderMap(field, field_name, tag, ow));
ow->EndObject();
@@ -218,48 +220,32 @@ StatusOr<uint32> ProtoStreamObjectSource::RenderMap(
const google::protobuf::Type* field_type =
typeinfo_->GetTypeByTypeUrl(field->type_url());
uint32 tag_to_return = 0;
- if (IsPackable(*field) &&
- list_tag ==
- WireFormatLite::MakeTag(field->number(),
- WireFormatLite::WIRETYPE_LENGTH_DELIMITED)) {
- RETURN_IF_ERROR(RenderPackedMapEntry(field_type, ow));
- tag_to_return = stream_->ReadTag();
- } else {
- do {
- RETURN_IF_ERROR(RenderMapEntry(field_type, ow));
- } while ((tag_to_return = stream_->ReadTag()) == list_tag);
- }
- return tag_to_return;
-}
-
-Status ProtoStreamObjectSource::RenderMapEntry(
- const google::protobuf::Type* type, ObjectWriter* ow) const {
- uint32 buffer32;
- stream_->ReadVarint32(&buffer32); // message length
- int old_limit = stream_->PushLimit(buffer32);
- string map_key;
- for (uint32 tag = stream_->ReadTag(); tag != 0; tag = stream_->ReadTag()) {
- const google::protobuf::Field* field = FindAndVerifyField(*type, tag);
- if (field == NULL) {
- WireFormat::SkipField(stream_, tag, NULL);
- continue;
- }
- // Map field numbers are key = 1 and value = 2
- if (field->number() == 1) {
- map_key = ReadFieldValueAsString(*field);
- } else if (field->number() == 2) {
- if (map_key.empty()) {
- return Status(util::error::INTERNAL, "Map key must be non-empty");
+ do {
+ // Render map entry message type.
+ uint32 buffer32;
+ stream_->ReadVarint32(&buffer32); // message length
+ int old_limit = stream_->PushLimit(buffer32);
+ string map_key;
+ for (uint32 tag = stream_->ReadTag(); tag != 0; tag = stream_->ReadTag()) {
+ const google::protobuf::Field* field =
+ FindAndVerifyField(*field_type, tag);
+ if (field == NULL) {
+ WireFormat::SkipField(stream_, tag, NULL);
+ continue;
+ }
+ // Map field numbers are key = 1 and value = 2
+ if (field->number() == 1) {
+ map_key = ReadFieldValueAsString(*field);
+ } else if (field->number() == 2) {
+ if (map_key.empty()) {
+ return Status(util::error::INTERNAL, "Map key must be non-empty");
+ }
+ RETURN_IF_ERROR(RenderField(field, map_key, ow));
}
- // Disable case normalization for map keys as they are just data. We
- // retain them intact.
- ow->DisableCaseNormalizationForNextKey();
- RETURN_IF_ERROR(RenderField(field, map_key, ow));
}
- }
- stream_->PopLimit(old_limit);
-
- return Status::OK;
+ stream_->PopLimit(old_limit);
+ } while ((tag_to_return = stream_->ReadTag()) == list_tag);
+ return tag_to_return;
}
Status ProtoStreamObjectSource::RenderPacked(
@@ -274,18 +260,6 @@ Status ProtoStreamObjectSource::RenderPacked(
return Status::OK;
}
-Status ProtoStreamObjectSource::RenderPackedMapEntry(
- const google::protobuf::Type* type, ObjectWriter* ow) const {
- uint32 length;
- stream_->ReadVarint32(&length);
- int old_limit = stream_->PushLimit(length);
- while (stream_->BytesUntilLimit() > 0) {
- RETURN_IF_ERROR(RenderMapEntry(type, ow));
- }
- stream_->PopLimit(old_limit);
- return Status::OK;
-}
-
Status ProtoStreamObjectSource::RenderTimestamp(
const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
StringPiece field_name, ObjectWriter* ow) {
@@ -601,10 +575,10 @@ Status ProtoStreamObjectSource::RenderAny(const ProtoStreamObjectSource* os,
// nested_type cannot be null at this time.
const google::protobuf::Type* nested_type = resolved_type.ValueOrDie();
- // We know the type so we can render it. Recursively parse the nested stream
- // using a nested ProtoStreamObjectSource using our nested type information.
google::protobuf::io::ArrayInputStream zero_copy_stream(value.data(), value.size());
google::protobuf::io::CodedInputStream in_stream(&zero_copy_stream);
+ // We know the type so we can render it. Recursively parse the nested stream
+ // using a nested ProtoStreamObjectSource using our nested type information.
ProtoStreamObjectSource nested_os(&in_stream, os->typeinfo_, *nested_type);
// We manually call start and end object here so we can inject the @type.
@@ -676,8 +650,7 @@ void ProtoStreamObjectSource::InitRendererMap() {
&ProtoStreamObjectSource::RenderString;
(*renderers_)["google.protobuf.BytesValue"] =
&ProtoStreamObjectSource::RenderBytes;
- (*renderers_)["google.protobuf.Any"] =
- &ProtoStreamObjectSource::RenderAny;
+ (*renderers_)["google.protobuf.Any"] = &ProtoStreamObjectSource::RenderAny;
(*renderers_)["google.protobuf.Struct"] =
&ProtoStreamObjectSource::RenderStruct;
(*renderers_)["google.protobuf.Value"] =
@@ -704,87 +677,118 @@ ProtoStreamObjectSource::FindTypeRenderer(const string& type_url) {
Status ProtoStreamObjectSource::RenderField(
const google::protobuf::Field* field, StringPiece field_name,
ObjectWriter* ow) const {
+ // Short-circuit message types as it tends to call WriteMessage recursively
+ // and ends up using a lot of stack space. Keep the stack usage of this
+ // message small in order to preserve stack space and not crash.
+ if (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) {
+ uint32 buffer32;
+ stream_->ReadVarint32(&buffer32); // message length
+ int old_limit = stream_->PushLimit(buffer32);
+ // Get the nested message type for this field.
+ const google::protobuf::Type* type =
+ typeinfo_->GetTypeByTypeUrl(field->type_url());
+ if (type == NULL) {
+ return Status(util::error::INTERNAL,
+ StrCat("Invalid configuration. Could not find the type: ",
+ field->type_url()));
+ }
+
+ // Short-circuit any special type rendering to save call-stack space.
+ const TypeRenderer* type_renderer = FindTypeRenderer(type->name());
+
+ bool use_type_renderer = type_renderer != NULL;
+
+ if (use_type_renderer) {
+ RETURN_IF_ERROR((*type_renderer)(this, *type, field_name, ow));
+ } else {
+ RETURN_IF_ERROR(WriteMessage(*type, field_name, 0, true, ow));
+ }
+ if (!stream_->ConsumedEntireMessage()) {
+ return Status(util::error::INVALID_ARGUMENT,
+ "Nested protocol message not parsed in its entirety.");
+ }
+ stream_->PopLimit(old_limit);
+ } else {
+ // Render all other non-message types.
+ return RenderNonMessageField(field, field_name, ow);
+ }
+ return Status::OK;
+}
+
+Status ProtoStreamObjectSource::RenderNonMessageField(
+ const google::protobuf::Field* field, StringPiece field_name,
+ ObjectWriter* ow) const {
+ // Temporary buffers of different types.
+ uint32 buffer32;
+ uint64 buffer64;
+ string strbuffer;
switch (field->kind()) {
case google::protobuf::Field_Kind_TYPE_BOOL: {
- uint64 buffer64;
stream_->ReadVarint64(&buffer64);
ow->RenderBool(field_name, buffer64 != 0);
break;
}
case google::protobuf::Field_Kind_TYPE_INT32: {
- uint32 buffer32;
stream_->ReadVarint32(&buffer32);
ow->RenderInt32(field_name, bit_cast<int32>(buffer32));
break;
}
case google::protobuf::Field_Kind_TYPE_INT64: {
- uint64 buffer64;
stream_->ReadVarint64(&buffer64);
ow->RenderInt64(field_name, bit_cast<int64>(buffer64));
break;
}
case google::protobuf::Field_Kind_TYPE_UINT32: {
- uint32 buffer32;
stream_->ReadVarint32(&buffer32);
ow->RenderUint32(field_name, bit_cast<uint32>(buffer32));
break;
}
case google::protobuf::Field_Kind_TYPE_UINT64: {
- uint64 buffer64;
stream_->ReadVarint64(&buffer64);
ow->RenderUint64(field_name, bit_cast<uint64>(buffer64));
break;
}
case google::protobuf::Field_Kind_TYPE_SINT32: {
- uint32 buffer32;
stream_->ReadVarint32(&buffer32);
ow->RenderInt32(field_name, WireFormatLite::ZigZagDecode32(buffer32));
break;
}
case google::protobuf::Field_Kind_TYPE_SINT64: {
- uint64 buffer64;
stream_->ReadVarint64(&buffer64);
ow->RenderInt64(field_name, WireFormatLite::ZigZagDecode64(buffer64));
break;
}
case google::protobuf::Field_Kind_TYPE_SFIXED32: {
- uint32 buffer32;
stream_->ReadLittleEndian32(&buffer32);
ow->RenderInt32(field_name, bit_cast<int32>(buffer32));
break;
}
case google::protobuf::Field_Kind_TYPE_SFIXED64: {
- uint64 buffer64;
stream_->ReadLittleEndian64(&buffer64);
ow->RenderInt64(field_name, bit_cast<int64>(buffer64));
break;
}
case google::protobuf::Field_Kind_TYPE_FIXED32: {
- uint32 buffer32;
stream_->ReadLittleEndian32(&buffer32);
ow->RenderUint32(field_name, bit_cast<uint32>(buffer32));
break;
}
case google::protobuf::Field_Kind_TYPE_FIXED64: {
- uint64 buffer64;
stream_->ReadLittleEndian64(&buffer64);
ow->RenderUint64(field_name, bit_cast<uint64>(buffer64));
break;
}
case google::protobuf::Field_Kind_TYPE_FLOAT: {
- uint32 buffer32;
stream_->ReadLittleEndian32(&buffer32);
ow->RenderFloat(field_name, bit_cast<float>(buffer32));
break;
}
case google::protobuf::Field_Kind_TYPE_DOUBLE: {
- uint64 buffer64;
stream_->ReadLittleEndian64(&buffer64);
ow->RenderDouble(field_name, bit_cast<double>(buffer64));
break;
}
case google::protobuf::Field_Kind_TYPE_ENUM: {
- uint32 buffer32;
stream_->ReadVarint32(&buffer32);
// If the field represents an explicit NULL value, render null.
@@ -811,40 +815,15 @@ Status ProtoStreamObjectSource::RenderField(
break;
}
case google::protobuf::Field_Kind_TYPE_STRING: {
- uint32 buffer32;
- string str;
stream_->ReadVarint32(&buffer32); // string size.
- stream_->ReadString(&str, buffer32);
- ow->RenderString(field_name, str);
+ stream_->ReadString(&strbuffer, buffer32);
+ ow->RenderString(field_name, strbuffer);
break;
}
case google::protobuf::Field_Kind_TYPE_BYTES: {
- uint32 buffer32;
stream_->ReadVarint32(&buffer32); // bytes size.
- string value;
- stream_->ReadString(&value, buffer32);
- ow->RenderBytes(field_name, value);
- break;
- }
- case google::protobuf::Field_Kind_TYPE_MESSAGE: {
- uint32 buffer32;
- stream_->ReadVarint32(&buffer32); // message length
- int old_limit = stream_->PushLimit(buffer32);
- // Get the nested message type for this field.
- const google::protobuf::Type* type =
- typeinfo_->GetTypeByTypeUrl(field->type_url());
- if (type == NULL) {
- return Status(util::error::INTERNAL,
- StrCat("Invalid configuration. Could not find the type: ",
- field->type_url()));
- }
-
- RETURN_IF_ERROR(WriteMessage(*type, field_name, 0, true, ow));
- if (!stream_->ConsumedEntireMessage()) {
- return Status(util::error::INVALID_ARGUMENT,
- "Nested protocol message not parsed in its entirety.");
- }
- stream_->PopLimit(old_limit);
+ stream_->ReadString(&strbuffer, buffer32);
+ ow->RenderBytes(field_name, strbuffer);
break;
}
default:
diff --git a/src/google/protobuf/util/internal/protostream_objectsource.h b/src/google/protobuf/util/internal/protostream_objectsource.h
index 3cd37aa1..78defa1d 100644
--- a/src/google/protobuf/util/internal/protostream_objectsource.h
+++ b/src/google/protobuf/util/internal/protostream_objectsource.h
@@ -122,20 +122,12 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource {
StringPiece name, uint32 list_tag,
ObjectWriter* ow) const;
- // Renders an entry in a map, advancing stream pointers appropriately.
- util::Status RenderMapEntry(const google::protobuf::Type* type,
- ObjectWriter* ow) const;
-
// Renders a packed repeating field. A packed field is stored as:
// {tag length item1 item2 item3} instead of the less efficient
// {tag item1 tag item2 tag item3}.
util::Status RenderPacked(const google::protobuf::Field* field,
ObjectWriter* ow) const;
- // Equivalent of RenderPacked, but for map entries.
- util::Status RenderPackedMapEntry(const google::protobuf::Type* type,
- ObjectWriter* ow) const;
-
// Renders a google.protobuf.Timestamp value to ObjectWriter
static util::Status RenderTimestamp(const ProtoStreamObjectSource* os,
const google::protobuf::Type& type,
@@ -210,6 +202,12 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource {
util::Status RenderField(const google::protobuf::Field* field,
StringPiece field_name, ObjectWriter* ow) const;
+ // Same as above but renders all non-message field types. Callers don't call
+ // this function directly. They just use RenderField.
+ util::Status RenderNonMessageField(const google::protobuf::Field* field,
+ StringPiece field_name,
+ ObjectWriter* ow) const;
+
// Reads field value according to Field spec in 'field' and returns the read
// value as string. This only works for primitive datatypes (no message
@@ -238,6 +236,7 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource {
// google::protobuf::Type of the message source.
const google::protobuf::Type& type_;
+
GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoStreamObjectSource);
};
diff --git a/src/google/protobuf/util/internal/protostream_objectsource_test.cc b/src/google/protobuf/util/internal/protostream_objectsource_test.cc
index f6e5ee7a..561f6763 100644
--- a/src/google/protobuf/util/internal/protostream_objectsource_test.cc
+++ b/src/google/protobuf/util/internal/protostream_objectsource_test.cc
@@ -327,9 +327,16 @@ TEST_P(ProtostreamObjectSourceTest, RepeatingPrimitives) {
DoTest(primitive, Primitive::descriptor());
}
+TEST_P(ProtostreamObjectSourceTest, CustomJsonName) {
+ Author author;
+ author.set_id(12345);
+
+ ow_.StartObject("")->RenderUint64("@id", 12345)->EndObject();
+ DoTest(author, Author::descriptor());
+}
+
TEST_P(ProtostreamObjectSourceTest, NestedMessage) {
Author* author = new Author();
- author->set_id(101L);
author->set_name("Tolstoy");
Book book;
book.set_title("My Book");
@@ -338,7 +345,6 @@ TEST_P(ProtostreamObjectSourceTest, NestedMessage) {
ow_.StartObject("")
->RenderString("title", "My Book")
->StartObject("author")
- ->RenderUint64("id", bit_cast<uint64>(101LL))
->RenderString("name", "Tolstoy")
->EndObject()
->EndObject();
diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.cc b/src/google/protobuf/util/internal/protostream_objectwriter.cc
index 0958997c..786bf0be 100644
--- a/src/google/protobuf/util/internal/protostream_objectwriter.cc
+++ b/src/google/protobuf/util/internal/protostream_objectwriter.cc
@@ -51,7 +51,6 @@ namespace util {
namespace converter {
using google::protobuf::internal::WireFormatLite;
-using google::protobuf::io::CodedOutputStream;
using util::error::INVALID_ARGUMENT;
using util::Status;
using util::StatusOr;
@@ -60,230 +59,31 @@ using util::StatusOr;
ProtoStreamObjectWriter::ProtoStreamObjectWriter(
TypeResolver* type_resolver, const google::protobuf::Type& type,
strings::ByteSink* output, ErrorListener* listener)
- : master_type_(type),
- typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
- own_typeinfo_(true),
- done_(false),
- element_(NULL),
- size_insert_(),
- output_(output),
- buffer_(),
- adapter_(&buffer_),
- stream_(new CodedOutputStream(&adapter_)),
- listener_(listener),
- invalid_depth_(0),
- tracker_(new ObjectLocationTracker()) {}
+ : ProtoWriter(type_resolver, type, output, listener),
+ master_type_(type),
+ current_(NULL) {}
ProtoStreamObjectWriter::ProtoStreamObjectWriter(
const TypeInfo* typeinfo, const google::protobuf::Type& type,
strings::ByteSink* output, ErrorListener* listener)
- : master_type_(type),
- typeinfo_(typeinfo),
- own_typeinfo_(false),
- done_(false),
- element_(NULL),
- size_insert_(),
- output_(output),
- buffer_(),
- adapter_(&buffer_),
- stream_(new CodedOutputStream(&adapter_)),
- listener_(listener),
- invalid_depth_(0),
- tracker_(new ObjectLocationTracker()) {}
+ : ProtoWriter(typeinfo, type, output, listener),
+ master_type_(type),
+ current_(NULL) {}
ProtoStreamObjectWriter::~ProtoStreamObjectWriter() {
- if (own_typeinfo_) {
- delete typeinfo_;
- }
- if (element_ == NULL) return;
+ if (current_ == NULL) 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(
- static_cast<BaseElement*>(element_.get())->pop<BaseElement>());
+ static_cast<BaseElement*>(current_.get())->pop<BaseElement>());
while (element != NULL) {
element.reset(element->pop<BaseElement>());
}
}
namespace {
-
-// Writes an INT32 field, including tag to the stream.
-inline Status WriteInt32(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<int32> i32 = data.ToInt32();
- if (i32.ok()) {
- WireFormatLite::WriteInt32(field_number, i32.ValueOrDie(), stream);
- }
- return i32.status();
-}
-
-// writes an SFIXED32 field, including tag, to the stream.
-inline Status WriteSFixed32(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<int32> i32 = data.ToInt32();
- if (i32.ok()) {
- WireFormatLite::WriteSFixed32(field_number, i32.ValueOrDie(), stream);
- }
- return i32.status();
-}
-
-// Writes an SINT32 field, including tag, to the stream.
-inline Status WriteSInt32(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<int32> i32 = data.ToInt32();
- if (i32.ok()) {
- WireFormatLite::WriteSInt32(field_number, i32.ValueOrDie(), stream);
- }
- return i32.status();
-}
-
-// Writes a FIXED32 field, including tag, to the stream.
-inline Status WriteFixed32(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<uint32> u32 = data.ToUint32();
- if (u32.ok()) {
- WireFormatLite::WriteFixed32(field_number, u32.ValueOrDie(), stream);
- }
- return u32.status();
-}
-
-// Writes a UINT32 field, including tag, to the stream.
-inline Status WriteUInt32(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<uint32> u32 = data.ToUint32();
- if (u32.ok()) {
- WireFormatLite::WriteUInt32(field_number, u32.ValueOrDie(), stream);
- }
- return u32.status();
-}
-
-// Writes an INT64 field, including tag, to the stream.
-inline Status WriteInt64(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<int64> i64 = data.ToInt64();
- if (i64.ok()) {
- WireFormatLite::WriteInt64(field_number, i64.ValueOrDie(), stream);
- }
- return i64.status();
-}
-
-// Writes an SFIXED64 field, including tag, to the stream.
-inline Status WriteSFixed64(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<int64> i64 = data.ToInt64();
- if (i64.ok()) {
- WireFormatLite::WriteSFixed64(field_number, i64.ValueOrDie(), stream);
- }
- return i64.status();
-}
-
-// Writes an SINT64 field, including tag, to the stream.
-inline Status WriteSInt64(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<int64> i64 = data.ToInt64();
- if (i64.ok()) {
- WireFormatLite::WriteSInt64(field_number, i64.ValueOrDie(), stream);
- }
- return i64.status();
-}
-
-// Writes a FIXED64 field, including tag, to the stream.
-inline Status WriteFixed64(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<uint64> u64 = data.ToUint64();
- if (u64.ok()) {
- WireFormatLite::WriteFixed64(field_number, u64.ValueOrDie(), stream);
- }
- return u64.status();
-}
-
-// Writes a UINT64 field, including tag, to the stream.
-inline Status WriteUInt64(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<uint64> u64 = data.ToUint64();
- if (u64.ok()) {
- WireFormatLite::WriteUInt64(field_number, u64.ValueOrDie(), stream);
- }
- return u64.status();
-}
-
-// Writes a DOUBLE field, including tag, to the stream.
-inline Status WriteDouble(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<double> d = data.ToDouble();
- if (d.ok()) {
- WireFormatLite::WriteDouble(field_number, d.ValueOrDie(), stream);
- }
- return d.status();
-}
-
-// Writes a FLOAT field, including tag, to the stream.
-inline Status WriteFloat(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<float> f = data.ToFloat();
- if (f.ok()) {
- WireFormatLite::WriteFloat(field_number, f.ValueOrDie(), stream);
- }
- return f.status();
-}
-
-// Writes a BOOL field, including tag, to the stream.
-inline Status WriteBool(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<bool> b = data.ToBool();
- if (b.ok()) {
- WireFormatLite::WriteBool(field_number, b.ValueOrDie(), stream);
- }
- return b.status();
-}
-
-// Writes a BYTES field, including tag, to the stream.
-inline Status WriteBytes(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<string> c = data.ToBytes();
- if (c.ok()) {
- WireFormatLite::WriteBytes(field_number, c.ValueOrDie(), stream);
- }
- return c.status();
-}
-
-// Writes a STRING field, including tag, to the stream.
-inline Status WriteString(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<string> s = data.ToString();
- if (s.ok()) {
- WireFormatLite::WriteString(field_number, s.ValueOrDie(), stream);
- }
- return s.status();
-}
-
-// 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);
- if (e.ok()) {
- WireFormatLite::WriteEnum(field_number, e.ValueOrDie(), stream);
- }
- return e.status();
-}
-
-// Given a google::protobuf::Type, returns the set of all required fields.
-std::set<const google::protobuf::Field*> GetRequiredFields(
- const google::protobuf::Type& type) {
- std::set<const google::protobuf::Field*> required;
- for (int i = 0; i < type.fields_size(); i++) {
- const google::protobuf::Field& field = type.fields(i);
- if (field.cardinality() ==
- google::protobuf::Field_Cardinality_CARDINALITY_REQUIRED) {
- required.insert(&field);
- }
- }
- return required;
-}
-
// Utility method to split a string representation of Timestamp or Duration and
// return the parts.
void SplitSecondsAndNanos(StringPiece input, StringPiece* seconds,
@@ -298,6 +98,78 @@ void SplitSecondsAndNanos(StringPiece input, StringPiece* seconds,
}
}
+Status GetNanosFromStringPiece(StringPiece s_nanos,
+ const char* parse_failure_message,
+ const char* exceeded_limit_message,
+ int32* nanos) {
+ *nanos = 0;
+
+ // Count the number of leading 0s and consume them.
+ int num_leading_zeros = 0;
+ while (s_nanos.Consume("0")) {
+ num_leading_zeros++;
+ }
+ int32 i_nanos = 0;
+ // 's_nanos' contains fractional seconds -- i.e. 'nanos' is equal to
+ // "0." + s_nanos.ToString() seconds. An int32 is used for the
+ // conversion to 'nanos', rather than a double, so that there is no
+ // loss of precision.
+ if (!s_nanos.empty() && !safe_strto32(s_nanos.ToString(), &i_nanos)) {
+ return Status(INVALID_ARGUMENT, parse_failure_message);
+ }
+ if (i_nanos > kNanosPerSecond || i_nanos < 0) {
+ return Status(INVALID_ARGUMENT, exceeded_limit_message);
+ }
+ // s_nanos should only have digits. No whitespace.
+ if (s_nanos.find_first_not_of("0123456789") != StringPiece::npos) {
+ return Status(INVALID_ARGUMENT, parse_failure_message);
+ }
+
+ if (i_nanos > 0) {
+ // 'scale' is the number of digits to the right of the decimal
+ // point in "0." + s_nanos.ToString()
+ int32 scale = num_leading_zeros + s_nanos.size();
+ // 'conversion' converts i_nanos into nanoseconds.
+ // conversion = kNanosPerSecond / static_cast<int32>(std::pow(10, scale))
+ // For efficiency, we precompute the conversion factor.
+ int32 conversion = 0;
+ switch (scale) {
+ case 1:
+ conversion = 100000000;
+ break;
+ case 2:
+ conversion = 10000000;
+ break;
+ case 3:
+ conversion = 1000000;
+ break;
+ case 4:
+ conversion = 100000;
+ break;
+ case 5:
+ conversion = 10000;
+ break;
+ case 6:
+ conversion = 1000;
+ break;
+ case 7:
+ conversion = 100;
+ break;
+ case 8:
+ conversion = 10;
+ break;
+ case 9:
+ conversion = 1;
+ break;
+ default:
+ return Status(INVALID_ARGUMENT, exceeded_limit_message);
+ }
+ *nanos = i_nanos * conversion;
+ }
+
+ return Status::OK;
+}
+
} // namespace
ProtoStreamObjectWriter::AnyWriter::AnyWriter(ProtoStreamObjectWriter* parent)
@@ -421,7 +293,7 @@ void ProtoStreamObjectWriter::AnyWriter::StartAny(const DataPiece& value) {
}
// Resolve the type url, and report an error if we failed to resolve it.
StatusOr<const google::protobuf::Type*> resolved_type =
- parent_->typeinfo_->ResolveTypeUrl(type_url_);
+ parent_->typeinfo()->ResolveTypeUrl(type_url_);
if (!resolved_type.ok()) {
parent_->InvalidValue("Any", resolved_type.status().error_message());
invalid_ = true;
@@ -440,8 +312,8 @@ void ProtoStreamObjectWriter::AnyWriter::StartAny(const DataPiece& value) {
// Create our object writer and initialize it with the first StartObject
// call.
- ow_.reset(new ProtoStreamObjectWriter(parent_->typeinfo_, *type, &output_,
- parent_->listener_));
+ ow_.reset(new ProtoStreamObjectWriter(parent_->typeinfo(), *type, &output_,
+ parent_->listener()));
ow_->StartObject("");
}
@@ -453,604 +325,431 @@ void ProtoStreamObjectWriter::AnyWriter::WriteAny() {
}
// Render the type_url and value fields directly to the stream.
// type_url has tag 1 and value has tag 2.
- WireFormatLite::WriteString(1, type_url_, parent_->stream_.get());
+ WireFormatLite::WriteString(1, type_url_, parent_->stream());
if (!data_.empty()) {
- WireFormatLite::WriteBytes(2, data_, parent_->stream_.get());
+ WireFormatLite::WriteBytes(2, data_, parent_->stream());
}
}
-ProtoStreamObjectWriter::ProtoElement::ProtoElement(
- const TypeInfo* typeinfo, const google::protobuf::Type& type,
- ProtoStreamObjectWriter* enclosing)
+ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter* enclosing,
+ ItemType item_type, bool is_placeholder,
+ bool is_list)
: BaseElement(NULL),
ow_(enclosing),
any_(),
- field_(NULL),
- typeinfo_(typeinfo),
- type_(type),
- required_fields_(GetRequiredFields(type)),
- is_repeated_type_(false),
- size_index_(-1),
- array_index_(-1),
- element_type_(GetElementType(type_)) {
- if (element_type_ == ANY) {
+ item_type_(item_type),
+ is_placeholder_(is_placeholder),
+ is_list_(is_list) {
+ if (item_type_ == ANY) {
any_.reset(new AnyWriter(ow_));
}
}
-ProtoStreamObjectWriter::ProtoElement::ProtoElement(
- ProtoStreamObjectWriter::ProtoElement* parent,
- const google::protobuf::Field* field, const google::protobuf::Type& type,
- ElementType element_type)
+ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter::Item* parent,
+ ItemType item_type, bool is_placeholder,
+ bool is_list)
: BaseElement(parent),
ow_(this->parent()->ow_),
any_(),
- field_(field),
- typeinfo_(this->parent()->typeinfo_),
- type_(type),
- is_repeated_type_(element_type == ProtoElement::LIST ||
- element_type == ProtoElement::STRUCT_LIST ||
- element_type == ProtoElement::MAP ||
- element_type == ProtoElement::STRUCT_MAP),
- size_index_(!is_repeated_type_ &&
- field->kind() ==
- google::protobuf::Field_Kind_TYPE_MESSAGE
- ? ow_->size_insert_.size()
- : -1),
- array_index_(is_repeated_type_ ? 0 : -1),
- element_type_(element_type) {
- if (!is_repeated_type_) {
- if (field->cardinality() ==
- google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
- // Update array_index_ if it is an explicit list.
- if (this->parent()->array_index_ >= 0) this->parent()->array_index_++;
- } else {
- this->parent()->RegisterField(field);
- }
- if (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) {
- 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
- // of any nested messages. We start with -start_pos here, so we only
- // need to add the final buffer position to it at the end.
- SizeInfo info = {start_pos, -start_pos};
- ow_->size_insert_.push_back(info);
- }
- }
- if (element_type == ANY) {
+ item_type_(item_type),
+ is_placeholder_(is_placeholder),
+ is_list_(is_list) {
+ if (item_type == ANY) {
any_.reset(new AnyWriter(ow_));
}
}
-ProtoStreamObjectWriter::ProtoElement*
-ProtoStreamObjectWriter::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());
- }
- // 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.
- // If size_index_ < 0, this is not a message, so no size field is added.
- if (size_index_ >= 0) {
- // Add the final buffer position to compute the total length of this
- // serialized message. The stored value (before this addition) already
- // contains the total length of the size fields of all nested messages
- // minus the initial buffer position.
- ow_->size_insert_[size_index_].size += ow_->stream_->ByteCount();
- // Calculate the length required to serialize the size field of the
- // message, and propagate this additional size information upward to
- // 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()) {
- // 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;
- }
- }
- }
- return BaseElement::pop<ProtoElement>();
-}
-
-void ProtoStreamObjectWriter::ProtoElement::RegisterField(
- const google::protobuf::Field* field) {
- if (!required_fields_.empty() &&
- field->cardinality() ==
- google::protobuf::Field_Cardinality_CARDINALITY_REQUIRED) {
- required_fields_.erase(field);
- }
-}
-
-string ProtoStreamObjectWriter::ProtoElement::ToString() const {
- if (parent() == NULL) return "";
- string loc = parent()->ToString();
- if (field_->cardinality() !=
- google::protobuf::Field_Cardinality_CARDINALITY_REPEATED ||
- parent()->field_ != field_) {
- string name = field_->name();
- int i = 0;
- while (i < name.size() && (ascii_isalnum(name[i]) || name[i] == '_')) ++i;
- if (i > 0 && i == name.size()) { // safe field name
- if (loc.empty()) {
- loc = name;
- } else {
- StrAppend(&loc, ".", name);
- }
- } else {
- StrAppend(&loc, "[\"", CEscape(name), "\"]");
- }
- }
- if (field_->cardinality() ==
- google::protobuf::Field_Cardinality_CARDINALITY_REPEATED &&
- array_index_ > 0) {
- StrAppend(&loc, "[", array_index_ - 1, "]");
- }
- return loc.empty() ? "." : loc;
-}
-
-bool ProtoStreamObjectWriter::ProtoElement::OneofIndexTaken(int32 index) {
- return ContainsKey(oneof_indices_, index);
-}
-
-void ProtoStreamObjectWriter::ProtoElement::TakeOneofIndex(int32 index) {
- InsertIfNotPresent(&oneof_indices_, index);
-}
-
-bool ProtoStreamObjectWriter::ProtoElement::InsertMapKeyIfNotPresent(
+bool ProtoStreamObjectWriter::Item::InsertMapKeyIfNotPresent(
StringPiece map_key) {
return InsertIfNotPresent(&map_keys_, map_key.ToString());
}
-inline void ProtoStreamObjectWriter::InvalidName(StringPiece unknown_name,
- StringPiece message) {
- listener_->InvalidName(location(), ToSnakeCase(unknown_name), message);
-}
-
-inline void ProtoStreamObjectWriter::InvalidValue(StringPiece type_name,
- StringPiece value) {
- listener_->InvalidValue(location(), type_name, value);
-}
-
-inline void ProtoStreamObjectWriter::MissingField(StringPiece missing_name) {
- listener_->MissingField(location(), missing_name);
-}
-
ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartObject(
StringPiece name) {
- // Starting the root message. Create the root ProtoElement and return.
- if (element_ == NULL) {
- if (!name.empty()) {
- InvalidName(name, "Root element should not be named.");
- }
- element_.reset(new ProtoElement(typeinfo_, master_type_, this));
+ if (invalid_depth() > 0) {
+ IncrementInvalidDepth();
+ return this;
+ }
+
+ // Starting the root message. Create the root Item and return.
+ // ANY message type does not need special handling, just set the ItemType
+ // to ANY.
+ if (current_ == NULL) {
+ ProtoWriter::StartObject(name);
+ current_.reset(new Item(
+ this, master_type_.name() == kAnyType ? Item::ANY : Item::MESSAGE,
+ false, false));
// If master type is a special type that needs extra values to be written to
// stream, we write those values.
if (master_type_.name() == kStructType) {
- StartStruct(NULL);
- } else if (master_type_.name() == kStructValueType) {
- // We got a StartObject call with google.protobuf.Value field. This means
- // we are starting an object within google.protobuf.Value type. The only
- // object within that type is a struct type. So start a struct.
- const google::protobuf::Field* field = StartStructValueInStruct(NULL);
- StartStruct(field);
+ // Struct has a map<string, Value> field called "fields".
+ // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto
+ // "fields": [
+ Push("fields", Item::MAP, true, true);
+ return this;
}
- return this;
- }
- const google::protobuf::Field* field = NULL;
- if (element_ != NULL && element_->IsAny()) {
- element_->any()->StartObject(name);
- return this;
- } else if (element_ != NULL &&
- (element_->IsMap() || element_->IsStructMap())) {
- if (!ValidMapKey(name)) {
- ++invalid_depth_;
+ if (master_type_.name() == kStructValueType) {
+ // We got a StartObject call with google.protobuf.Value field. The only
+ // object within that type is a struct type. So start a struct.
+ //
+ // The struct field in Value type is named "struct_value"
+ // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto
+ // Also start the map field "fields" within the struct.
+ // "struct_value": {
+ // "fields": [
+ Push("struct_value", Item::MESSAGE, true, false);
+ Push("fields", Item::MAP, true, true);
return this;
}
- field = StartMapEntry(name);
- if (element_->IsStructMapEntry()) {
- // If the top element is a map entry, this means we are starting another
- // struct within a struct.
- field = StartStructValueInStruct(field);
+ if (master_type_.name() == kStructListValueType) {
+ InvalidValue(kStructListValueType,
+ "Cannot start root message with ListValue.");
}
- } else if (element_ != NULL && element_->IsStructList()) {
- // If the top element is a list, then we are starting a list field within a
- // struct.
- field = Lookup(name);
- field = StartStructValueInStruct(field);
- } else {
- field = BeginNamed(name, false);
- }
- if (field == NULL) {
- return this;
- }
- const google::protobuf::Type* type = LookupType(field);
- if (type == NULL) {
- ++invalid_depth_;
- InvalidName(name,
- StrCat("Missing descriptor for field: ", field->type_url()));
return this;
}
- // Check to see if this field is a oneof and that no oneof in that group has
- // already been set.
- if (!ValidOneof(*field, name)) {
- ++invalid_depth_;
+ // Send all ANY events to AnyWriter.
+ if (current_->IsAny()) {
+ current_->any()->StartObject(name);
return this;
}
- if (field->type_url() == GetFullTypeWithUrl(kStructType)) {
- // Start a struct object.
- StartStruct(field);
- } else if (field->type_url() == GetFullTypeWithUrl(kStructValueType)) {
- // We got a StartObject call with google.protobuf.Value field. This means we
- // are starting an object within google.protobuf.Value type. The only object
- // within that type is a struct type. So start a struct.
- field = StartStructValueInStruct(field);
- StartStruct(field);
- } else if (field->type_url() == GetFullTypeWithUrl(kAnyType)) {
- // Begin an Any. We can't do the real work till we get the @type field.
- WriteTag(*field);
- element_.reset(
- new ProtoElement(element_.release(), field, *type, ProtoElement::ANY));
- } else if (IsMap(*field)) {
- // Begin a map.
- // A map is triggered by a StartObject() call if the current field has a map
- // type. Map values are written to proto in a manner detailed in comments
- // above StartMapEntry() function.
- element_.reset(
- new ProtoElement(element_.release(), field, *type, ProtoElement::MAP));
- } else {
- WriteTag(*field);
- element_.reset(new ProtoElement(element_.release(), field, *type,
- ProtoElement::MESSAGE));
- }
- return this;
-}
-
-// Proto3 maps are represented on the wire as a message with
-// "key" and a "value".
-//
-// For example, the syntax:
-// map<key_type, value_type> map_field = N;
-//
-// is represented as:
-// message MapFieldEntry {
-// option map_entry = true; // marks the map construct in the descriptor
-//
-// key_type key = 1;
-// value_type value = 2;
-// }
-// repeated MapFieldEntry map_field = N;
-//
-// See go/proto3-maps for more information.
-const google::protobuf::Field* ProtoStreamObjectWriter::StartMapEntry(
- StringPiece name) {
- // top of stack is already a map field
- const google::protobuf::Field* field = element_->field();
- const google::protobuf::Type& type = element_->type();
- // If we come from a regular map, use MAP_ENTRY or if we come from a struct,
- // use STRUCT_MAP_ENTRY. These values are used later in StartObject/StartList
- // or RenderDataPiece for making appropriate decisions.
- ProtoElement::ElementType element_type = element_->IsStructMap()
- ? ProtoElement::STRUCT_MAP_ENTRY
- : ProtoElement::MAP_ENTRY;
- WriteTag(*field);
- element_.reset(
- new ProtoElement(element_.release(), field, type, element_type));
- RenderDataPiece("key", DataPiece(name));
- return BeginNamed("value", false);
-}
+ // If we are within a map, we render name as keys and send StartObject to the
+ // value field.
+ if (current_->IsMap()) {
+ if (!ValidMapKey(name)) {
+ IncrementInvalidDepth();
+ return this;
+ }
-// Starts a google.protobuf.Struct.
-// 'field' represents a field in a message of type google.protobuf.Struct. A
-// struct contains a map with name 'fields'. This function starts this map as
-// well.
-// When 'field' is NULL, it means that the top level message is of struct
-// type.
-void ProtoStreamObjectWriter::StartStruct(
- const google::protobuf::Field* field) {
- const google::protobuf::Type* type = NULL;
- if (field) {
- type = LookupType(field);
- WriteTag(*field);
- element_.reset(new ProtoElement(element_.release(), field, *type,
- ProtoElement::STRUCT));
- }
- const google::protobuf::Field* struct_field = BeginNamed("fields", false);
+ // Map is a repeated field of message type with a "key" and a "value" field.
+ // https://developers.google.com/protocol-buffers/docs/proto3?hl=en#maps
+ // message MapFieldEntry {
+ // key_type key = 1;
+ // value_type value = 2;
+ // }
+ //
+ // repeated MapFieldEntry map_field = N;
+ //
+ // That means, we render the following element within a list (hence no
+ // name):
+ // { "key": "<name>", "value": {
+ Push("", Item::MESSAGE, false, false);
+ ProtoWriter::RenderDataPiece("key", DataPiece(name));
+ Push("value", Item::MESSAGE, true, false);
+
+ // Make sure we are valid so far after starting map fields.
+ if (invalid_depth() > 0) return this;
+
+ // If top of stack is g.p.Struct type, start the struct the map field within
+ // it.
+ if (element() != NULL && IsStruct(*element()->parent_field())) {
+ // Render "fields": [
+ Push("fields", Item::MAP, true, true);
+ return this;
+ }
- if (!struct_field) {
- // It is a programmatic error if this happens. Log an error.
- GOOGLE_LOG(ERROR) << "Invalid internal state. Cannot find 'fields' within "
- << (field ? field->type_url() : "google.protobuf.Struct");
- return;
+ // If top of stack is g.p.Value type, start the Struct within it.
+ if (element() != NULL && IsStructValue(*element()->parent_field())) {
+ // Render
+ // "struct_value": {
+ // "fields": [
+ Push("struct_value", Item::MESSAGE, true, false);
+ Push("fields", Item::MAP, true, true);
+ }
+ return this;
}
- type = LookupType(struct_field);
- element_.reset(new ProtoElement(element_.release(), struct_field, *type,
- ProtoElement::STRUCT_MAP));
-}
+ const google::protobuf::Field* field = BeginNamed(name, false);
+ if (field == NULL) return this;
-// Starts a "struct_value" within struct.proto's google.protobuf.Value type.
-// 'field' should be of the type google.protobuf.Value.
-// Returns the field identifying "struct_value" within the given field.
-//
-// If field is NULL, then we are starting struct_value at the top-level, in
-// this case skip writing any tag information for the passed field.
-const google::protobuf::Field*
-ProtoStreamObjectWriter::StartStructValueInStruct(
- const google::protobuf::Field* field) {
- if (field) {
- const google::protobuf::Type* type = LookupType(field);
- WriteTag(*field);
- element_.reset(new ProtoElement(element_.release(), field, *type,
- ProtoElement::STRUCT_VALUE));
- }
- return BeginNamed("struct_value", false);
-}
-
-// Starts a "list_value" within struct.proto's google.protobuf.Value type.
-// 'field' should be of the type google.protobuf.Value.
-// Returns the field identifying "list_value" within the given field.
-//
-// If field is NULL, then we are starting list_value at the top-level, in
-// this case skip writing any tag information for the passed field.
-const google::protobuf::Field* ProtoStreamObjectWriter::StartListValueInStruct(
- const google::protobuf::Field* field) {
- if (field) {
- const google::protobuf::Type* type = LookupType(field);
- WriteTag(*field);
- element_.reset(new ProtoElement(element_.release(), field, *type,
- ProtoElement::STRUCT_VALUE));
+ if (IsStruct(*field)) {
+ // Start a struct object.
+ // Render
+ // "<name>": {
+ // "fields": {
+ Push(name, Item::MESSAGE, false, false);
+ Push("fields", Item::MAP, true, true);
+ return this;
}
- const google::protobuf::Field* list_value = BeginNamed("list_value", false);
- if (!list_value) {
- // It is a programmatic error if this happens. Log an error.
- GOOGLE_LOG(ERROR) << "Invalid internal state. Cannot find 'list_value' within "
- << (field ? field->type_url() : "google.protobuf.Value");
- return field;
+ if (IsStructValue(*field)) {
+ // We got a StartObject call with google.protobuf.Value field. The only
+ // object within that type is a struct type. So start a struct.
+ // Render
+ // "<name>": {
+ // "struct_value": {
+ // "fields": {
+ Push(name, Item::MESSAGE, false, false);
+ Push("struct_value", Item::MESSAGE, true, false);
+ Push("fields", Item::MAP, true, true);
+ return this;
}
- return StartRepeatedValuesInListValue(list_value);
-}
-
-// Starts the repeated "values" field in struct.proto's
-// google.protobuf.ListValue type. 'field' should be of type
-// google.protobuf.ListValue.
-//
-// If field is NULL, then we are starting ListValue at the top-level, in
-// this case skip writing any tag information for the passed field.
-const google::protobuf::Field*
-ProtoStreamObjectWriter::StartRepeatedValuesInListValue(
- const google::protobuf::Field* field) {
- if (field) {
- const google::protobuf::Type* type = LookupType(field);
- WriteTag(*field);
- element_.reset(new ProtoElement(element_.release(), field, *type,
- ProtoElement::STRUCT_LIST_VALUE));
+ if (IsMap(*field)) {
+ // Begin a map. A map is triggered by a StartObject() call if the current
+ // field has a map type.
+ // A map type is always repeated, hence set is_list to true.
+ // Render
+ // "<name>": [
+ Push(name, Item::MAP, false, true);
+ return this;
}
- return BeginNamed("values", true);
-}
-void ProtoStreamObjectWriter::SkipElements() {
- if (element_ == NULL) return;
-
- ProtoElement::ElementType element_type = element_->element_type();
- while (element_type == ProtoElement::STRUCT ||
- element_type == ProtoElement::STRUCT_LIST_VALUE ||
- element_type == ProtoElement::STRUCT_VALUE ||
- element_type == ProtoElement::STRUCT_MAP_ENTRY ||
- element_type == ProtoElement::MAP_ENTRY) {
- element_.reset(element_->pop());
- element_type =
- element_ != NULL ? element_->element_type() : ProtoElement::MESSAGE;
- }
+ // A regular message type. Pass it directly to ProtoWriter.
+ // Render
+ // "<name>": {
+ Push(name, IsAny(*field) ? Item::ANY : Item::MESSAGE, false, false);
+ return this;
}
ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndObject() {
- if (invalid_depth_ > 0) {
- --invalid_depth_;
+ if (invalid_depth() > 0) {
+ DecrementInvalidDepth();
return this;
}
- if (element_ != NULL && element_->IsAny()) {
- if (element_->any()->EndObject()) {
- return this;
- }
- }
- if (element_ != NULL) {
- element_.reset(element_->pop());
- }
- // Skip sentinel elements added to keep track of new proto3 types - map,
- // struct.
- SkipElements();
+ if (current_ == NULL) return this;
-
- // If ending the root element,
- // then serialize the full message with calculated sizes.
- if (element_ == NULL) {
- WriteRootMessage();
+ if (current_->IsAny()) {
+ if (current_->any()->EndObject()) return this;
}
+
+ Pop();
+
return this;
}
ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartList(StringPiece name) {
- const google::protobuf::Field* field = NULL;
+ if (invalid_depth() > 0) {
+ IncrementInvalidDepth();
+ return this;
+ }
+
// Since we cannot have a top-level repeated item in protobuf, the only way
- // element_ can be null when here is when we start a top-level list
- // google.protobuf.ListValue.
- if (element_ == NULL) {
+ // this is valid is if we start a special type google.protobuf.ListValue or
+ // google.protobuf.Value.
+ if (current_ == NULL) {
if (!name.empty()) {
InvalidName(name, "Root element should not be named.");
+ IncrementInvalidDepth();
+ return this;
}
- element_.reset(new ProtoElement(typeinfo_, master_type_, this));
// If master type is a special type that needs extra values to be written to
// stream, we write those values.
if (master_type_.name() == kStructValueType) {
// We got a StartList with google.protobuf.Value master type. This means
// we have to start the "list_value" within google.protobuf.Value.
- field = StartListValueInStruct(NULL);
- } else if (master_type_.name() == kStructListValueType) {
+ //
+ // See
+ // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto
+ //
+ // Render
+ // "<name>": {
+ // "list_value": {
+ // "values": [ // Start this list.
+ ProtoWriter::StartObject(name);
+ current_.reset(new Item(this, Item::MESSAGE, false, false));
+ Push("list_value", Item::MESSAGE, true, false);
+ Push("values", Item::MESSAGE, true, true);
+ return this;
+ }
+
+ if (master_type_.name() == kStructListValueType) {
// We got a StartList with google.protobuf.ListValue master type. This
// means we have to start the "values" within google.protobuf.ListValue.
- field = StartRepeatedValuesInListValue(NULL);
+ //
+ // Render
+ // "<name>": {
+ // "values": [ // Start this list.
+ ProtoWriter::StartObject(name);
+ current_.reset(new Item(this, Item::MESSAGE, false, false));
+ Push("values", Item::MESSAGE, true, true);
+ return this;
}
- // field is NULL when master_type_ is anything other than
- // google.protobuf.Value or google.protobuf.ListValue.
- if (field) {
- const google::protobuf::Type* type = LookupType(field);
- element_.reset(new ProtoElement(element_.release(), field, *type,
- ProtoElement::STRUCT_LIST));
- }
+ // Send the event to ProtoWriter so proper errors can be reported.
+ //
+ // Render a regular list:
+ // "<name>": [
+ ProtoWriter::StartList(name);
+ current_.reset(new Item(this, Item::MESSAGE, false, true));
return this;
}
- if (element_->IsAny()) {
- element_->any()->StartList(name);
+ if (current_->IsAny()) {
+ current_->any()->StartList(name);
return this;
}
- // The type of element we push to stack.
- ProtoElement::ElementType element_type = ProtoElement::LIST;
- // Check if we need to start a map. This can heppen when there is either a map
- // or a struct type within a list.
- if (element_->IsMap() || element_->IsStructMap()) {
+ // If the top of stack is a map, we are starting a list value within a map.
+ // Since map does not allow repeated values, this can only happen when the map
+ // value is of a special type that renders a list in JSON. These can be one
+ // of 3 cases:
+ // i. We are rendering a list value within google.protobuf.Struct
+ // ii. We are rendering a list value within google.protobuf.Value
+ // iii. We are rendering a list value with type google.protobuf.ListValue.
+ if (current_->IsMap()) {
if (!ValidMapKey(name)) {
- ++invalid_depth_;
+ IncrementInvalidDepth();
return this;
}
- field = StartMapEntry(name);
- if (field == NULL) return this;
+ // Start the repeated map entry object.
+ // Render
+ // { "key": "<name>", "value": {
+ Push("", Item::MESSAGE, false, false);
+ ProtoWriter::RenderDataPiece("key", DataPiece(name));
+ Push("value", Item::MESSAGE, true, false);
+
+ // Make sure we are valid after pushing all above items.
+ if (invalid_depth() > 0) return this;
+
+ // case i and ii above. Start "list_value" field within g.p.Value
+ if (element() != NULL && element()->parent_field() != NULL) {
+ // Render
+ // "list_value": {
+ // "values": [ // Start this list
+ if (IsStructValue(*element()->parent_field())) {
+ Push("list_value", Item::MESSAGE, true, false);
+ Push("values", Item::MESSAGE, true, true);
+ return this;
+ }
- if (element_->IsStructMapEntry()) {
- // If the top element is a map entry, this means we are starting a list
- // within a struct or a map.
- // An example sequence of calls would be
- // StartObject -> StartList
- field = StartListValueInStruct(field);
- if (field == NULL) return this;
+ // Render
+ // "values": [
+ if (IsStructListValue(*element()->parent_field())) {
+ // case iii above. Bind directly to g.p.ListValue
+ Push("values", Item::MESSAGE, true, true);
+ return this;
+ }
}
- element_type = ProtoElement::STRUCT_LIST;
- } else if (element_->IsStructList()) {
- // If the top element is a STRUCT_LIST, this means we are starting a list
- // within the current list (inside a struct).
- // An example call sequence would be
- // StartObject -> StartList -> StartList
- // with StartObject starting a struct.
+ // Report an error.
+ InvalidValue("Map", StrCat("Cannot have repeated items ('", name,
+ "') within a map."));
+ return this;
+ }
- // Lookup the last list type in element stack as we are adding an element of
- // the same type.
- field = Lookup(name);
- if (field == NULL) return this;
+ // When name is empty and stack is not empty, we are rendering an item within
+ // a list.
+ if (name.empty()) {
+ if (element() != NULL && element()->parent_field() != NULL) {
+ if (IsStructValue(*element()->parent_field())) {
+ // Since it is g.p.Value, we bind directly to the list_value.
+ // Render
+ // { // g.p.Value item within the list
+ // "list_value": {
+ // "values": [
+ Push("", Item::MESSAGE, false, false);
+ Push("list_value", Item::MESSAGE, true, false);
+ Push("values", Item::MESSAGE, true, true);
+ return this;
+ }
- field = StartListValueInStruct(field);
- if (field == NULL) return this;
+ if (IsStructListValue(*element()->parent_field())) {
+ // Since it is g.p.ListValue, we bind to it directly.
+ // Render
+ // { // g.p.ListValue item within the list
+ // "values": [
+ Push("", Item::MESSAGE, false, false);
+ Push("values", Item::MESSAGE, true, true);
+ return this;
+ }
+ }
- element_type = ProtoElement::STRUCT_LIST;
- } else {
- // Lookup field corresponding to 'name'. If it is a google.protobuf.Value
- // or google.protobuf.ListValue type, then StartList is a valid call, start
- // this list.
- // We cannot use Lookup() here as it will produce InvalidName() error if the
- // field is not found. We do not want to error here as it would cause us to
- // report errors twice, once here and again later in BeginNamed() call.
- // Also we ignore if the field is not found here as it is caught later.
- field = typeinfo_->FindField(&element_->type(), name);
-
- // Only check for oneof collisions on the first StartList call. We identify
- // the first call with !name.empty() check. Subsequent list element calls
- // will not have the name filled.
- if (!name.empty() && field && !ValidOneof(*field, name)) {
- ++invalid_depth_;
+ // Pass the event to underlying ProtoWriter.
+ Push(name, Item::MESSAGE, false, true);
+ return this;
+ }
+
+ // name is not empty
+ const google::protobuf::Field* field = Lookup(name);
+ if (field == NULL) {
+ IncrementInvalidDepth();
+ return this;
+ }
+
+ if (IsStructValue(*field)) {
+ // If g.p.Value is repeated, start that list. Otherwise, start the
+ // "list_value" within it.
+ if (IsRepeated(*field)) {
+ // Render it just like a regular repeated field.
+ // "<name>": [
+ Push(name, Item::MESSAGE, false, true);
return this;
}
- // It is an error to try to bind to map, which behind the scenes is a list.
- if (field && IsMap(*field)) {
- // Push field to stack for error location tracking & reporting.
- element_.reset(new ProtoElement(element_.release(), field,
- *LookupType(field),
- ProtoElement::MESSAGE));
- InvalidValue("Map", "Cannot bind a list to map.");
- ++invalid_depth_;
- element_.reset(element_->pop());
+ // Start the "list_value" field.
+ // Render
+ // "<name>": {
+ // "list_value": {
+ // "values": [
+ Push(name, Item::MESSAGE, false, false);
+ Push("list_value", Item::MESSAGE, true, false);
+ Push("values", Item::MESSAGE, true, true);
+ return this;
+ }
+
+ if (IsStructListValue(*field)) {
+ // If g.p.ListValue is repeated, start that list. Otherwise, start the
+ // "values" within it.
+ if (IsRepeated(*field)) {
+ // Render it just like a regular repeated field.
+ // "<name>": [
+ Push(name, Item::MESSAGE, false, true);
return this;
}
- if (field && field->type_url() == GetFullTypeWithUrl(kStructValueType)) {
- // There are 2 cases possible:
- // a. g.p.Value is repeated
- // b. g.p.Value is not repeated
- //
- // For case (a), the StartList should bind to the repeated g.p.Value.
- // For case (b), the StartList should bind to g.p.ListValue within the
- // g.p.Value.
- //
- // This means, for case (a), we treat it just like any other repeated
- // message, except we would apply an appropriate element_type so future
- // Start or Render calls are routed appropriately.
- if (field->cardinality() !=
- google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
- field = StartListValueInStruct(field);
- }
- element_type = ProtoElement::STRUCT_LIST;
- } else if (field &&
- field->type_url() == GetFullTypeWithUrl(kStructListValueType)) {
- // We got a StartList with google.protobuf.ListValue master type. This
- // means we have to start the "values" within google.protobuf.ListValue.
- field = StartRepeatedValuesInListValue(field);
- } else {
- // If no special types are to be bound, fall back to normal processing of
- // StartList.
- field = BeginNamed(name, true);
- }
- if (field == NULL) return this;
+ // Start the "values" field within g.p.ListValue.
+ // Render
+ // "<name>": {
+ // "values": [
+ Push(name, Item::MESSAGE, false, false);
+ Push("values", Item::MESSAGE, true, true);
+ return this;
}
- const google::protobuf::Type* type = LookupType(field);
- if (type == NULL) {
- ++invalid_depth_;
- InvalidName(name,
- StrCat("Missing descriptor for field: ", field->type_url()));
+ // If we are here, the field should be repeated. Report an error otherwise.
+ if (!IsRepeated(*field)) {
+ IncrementInvalidDepth();
+ InvalidName(name, "Proto field is not repeating, cannot start list.");
return this;
}
- element_.reset(
- new ProtoElement(element_.release(), field, *type, element_type));
+ if (IsMap(*field)) {
+ InvalidValue("Map",
+ StrCat("Cannot bind a list to map for field '", name, "'."));
+ IncrementInvalidDepth();
+ return this;
+ }
+
+ // Pass the event to ProtoWriter.
+ // Render
+ // "<name>": [
+ Push(name, Item::MESSAGE, false, true);
return this;
}
ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndList() {
- if (invalid_depth_ > 0) {
- --invalid_depth_;
- } else if (element_ != NULL) {
- if (element_->IsAny()) {
- element_->any()->EndList();
- } else {
- element_.reset(element_->pop());
- // Skip sentinel elements added to keep track of new proto3 types - map,
- // struct.
- SkipElements();
- }
+ if (invalid_depth() > 0) {
+ DecrementInvalidDepth();
+ return this;
}
- // When element_ is NULL, we have reached the root message type. Write out
- // the bytes.
- if (element_ == NULL) {
- WriteRootMessage();
+ if (current_ == NULL) return this;
+
+ if (current_->IsAny()) {
+ current_->any()->EndList();
+ return this;
}
+
+ Pop();
return this;
}
@@ -1083,7 +782,7 @@ Status ProtoStreamObjectWriter::RenderStructValue(ProtoStreamObjectWriter* ow,
"null values are supported.");
}
}
- ow->RenderDataPiece(struct_field_name, data);
+ ow->ProtoWriter::RenderDataPiece(struct_field_name, data);
return Status::OK;
}
@@ -1105,15 +804,15 @@ Status ProtoStreamObjectWriter::RenderTimestamp(ProtoStreamObjectWriter* ow,
}
- ow->RenderDataPiece("seconds", DataPiece(seconds));
- ow->RenderDataPiece("nanos", DataPiece(nanos));
+ ow->ProtoWriter::RenderDataPiece("seconds", DataPiece(seconds));
+ ow->ProtoWriter::RenderDataPiece("nanos", DataPiece(nanos));
return Status::OK;
}
static inline util::Status RenderOneFieldPath(ProtoStreamObjectWriter* ow,
StringPiece path) {
- ow->RenderDataPiece("paths",
- DataPiece(ConvertFieldMaskPath(path, &ToSnakeCase)));
+ ow->ProtoWriter::RenderDataPiece(
+ "paths", DataPiece(ConvertFieldMaskPath(path, &ToSnakeCase)));
return Status::OK;
}
@@ -1162,224 +861,133 @@ Status ProtoStreamObjectWriter::RenderDuration(ProtoStreamObjectWriter* ow,
"Invalid duration format, failed to parse seconds");
}
- double d_nanos = 0;
- if (!safe_strtod("0." + s_nanos.ToString(), &d_nanos)) {
- return Status(INVALID_ARGUMENT,
- "Invalid duration format, failed to parse nanos seconds");
+ int32 nanos = 0;
+ Status nanos_status = GetNanosFromStringPiece(
+ s_nanos, "Invalid duration format, failed to parse nano seconds",
+ "Duration value exceeds limits", &nanos);
+ if (!nanos_status.ok()) {
+ return nanos_status;
}
+ nanos = sign * nanos;
- int32 nanos = sign * static_cast<int32>(d_nanos * kNanosPerSecond);
int64 seconds = sign * unsigned_seconds;
-
if (seconds > kMaxSeconds || seconds < kMinSeconds ||
nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) {
return Status(INVALID_ARGUMENT, "Duration value exceeds limits");
}
- ow->RenderDataPiece("seconds", DataPiece(seconds));
- ow->RenderDataPiece("nanos", DataPiece(nanos));
+ ow->ProtoWriter::RenderDataPiece("seconds", DataPiece(seconds));
+ ow->ProtoWriter::RenderDataPiece("nanos", DataPiece(nanos));
return Status::OK;
}
Status ProtoStreamObjectWriter::RenderWrapperType(ProtoStreamObjectWriter* ow,
const DataPiece& data) {
- ow->RenderDataPiece("value", data);
+ ow->ProtoWriter::RenderDataPiece("value", data);
return Status::OK;
}
ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece(
StringPiece name, const DataPiece& data) {
Status status;
- if (invalid_depth_ > 0) return this;
- if (element_ != NULL && element_->IsAny()) {
- element_->any()->RenderDataPiece(name, data);
+ if (invalid_depth() > 0) return this;
+
+ if (current_ == NULL) {
+ const TypeRenderer* type_renderer =
+ FindTypeRenderer(GetFullTypeWithUrl(master_type_.name()));
+ if (type_renderer == NULL) {
+ InvalidName(name, "Root element must be a message.");
+ return this;
+ }
+ // Render the special type.
+ // "<name>": {
+ // ... Render special type ...
+ // }
+ ProtoWriter::StartObject(name);
+ status = (*type_renderer)(this, data);
+ if (!status.ok()) {
+ InvalidValue(master_type_.name(),
+ StrCat("Field '", name, "', ", status.error_message()));
+ }
+ ProtoWriter::EndObject();
+ return this;
+ }
+
+ if (current_->IsAny()) {
+ current_->any()->RenderDataPiece(name, data);
return this;
}
const google::protobuf::Field* field = NULL;
- string type_url;
- bool is_map_entry = false;
- // We are at the root when element_ == NULL.
- if (element_ == NULL) {
- type_url = GetFullTypeWithUrl(master_type_.name());
- } else {
- if (element_->IsMap() || element_->IsStructMap()) {
- if (!ValidMapKey(name)) return this;
- is_map_entry = true;
- field = StartMapEntry(name);
- } else {
- field = Lookup(name);
- }
+ if (current_->IsMap()) {
+ if (!ValidMapKey(name)) return this;
+
+ // Render an item in repeated map list.
+ // { "key": "<name>", "value":
+ Push("", Item::MESSAGE, false, false);
+ ProtoWriter::RenderDataPiece("key", DataPiece(name));
+ field = Lookup("value");
if (field == NULL) {
+ GOOGLE_LOG(DFATAL) << "Map does not have a value field.";
+ return this;
+ }
+
+ const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url());
+ if (type_renderer != NULL) {
+ // Map's value type is a special type. Render it like a message:
+ // "value": {
+ // ... Render special type ...
+ // }
+ Push("value", Item::MESSAGE, true, false);
+ status = (*type_renderer)(this, data);
+ if (!status.ok()) {
+ InvalidValue(field->type_url(),
+ StrCat("Field '", name, "', ", status.error_message()));
+ }
+ Pop();
return this;
}
- // Check to see if this field is a oneof and that no oneof in that group has
- // already been set.
- if (!ValidOneof(*field, name)) return this;
+ // If we are rendering explicit null values and the backend proto field is
+ // not of the google.protobuf.NullType type, we do nothing.
+ if (data.type() == DataPiece::TYPE_NULL &&
+ field->type_url() != kStructNullValueTypeUrl) {
+ return this;
+ }
- type_url = field->type_url();
+ // Render the map value as a primitive type.
+ ProtoWriter::RenderDataPiece("value", data);
+ Pop();
+ return this;
}
- // Check if there are any well known type renderers available for type_url.
- const TypeRenderer* type_renderer = FindTypeRenderer(type_url);
+ field = Lookup(name);
+ if (field == NULL) return this;
+
+ // Check if the field is of special type. Render it accordingly if so.
+ const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url());
if (type_renderer != NULL) {
- // Push the current element to stack so lookups in type_renderer will
- // find the fields. We do an EndObject soon after, which pops this. This is
- // safe because all well-known types are messages.
- if (element_ == NULL) {
- element_.reset(new ProtoElement(typeinfo_, master_type_, this));
- } else {
- if (field) {
- WriteTag(*field);
- const google::protobuf::Type* type = LookupType(field);
- element_.reset(new ProtoElement(element_.release(), field, *type,
- ProtoElement::MESSAGE));
- }
- }
+ Push(name, Item::MESSAGE, false, false);
status = (*type_renderer)(this, data);
if (!status.ok()) {
- InvalidValue(type_url,
+ InvalidValue(field->type_url(),
StrCat("Field '", name, "', ", status.error_message()));
}
- EndObject();
- return this;
- } else if (element_ == NULL) { // no message type found at root
- element_.reset(new ProtoElement(typeinfo_, master_type_, this));
- InvalidName(name, "Root element must be a message.");
+ Pop();
return this;
}
- if (field == NULL) {
- return this;
- }
- const google::protobuf::Type* type = LookupType(field);
- if (type == NULL) {
- InvalidName(name,
- StrCat("Missing descriptor for field: ", field->type_url()));
+ // If we are rendering explicit null values and the backend proto field is
+ // not of the google.protobuf.NullType type, we do nothing.
+ if (data.type() == DataPiece::TYPE_NULL &&
+ field->type_url() != kStructNullValueTypeUrl) {
return this;
}
- // Whether we should pop at the end. Set to true if the data field is a
- // message type, which can happen in case of struct values.
- bool should_pop = false;
-
- RenderSimpleDataPiece(*field, *type, data);
-
- if (should_pop && element_ != NULL) {
- element_.reset(element_->pop());
- }
-
- if (is_map_entry) {
- // Ending map is the same as ending an object.
- EndObject();
- }
+ ProtoWriter::RenderDataPiece(name, data);
return this;
}
-void ProtoStreamObjectWriter::RenderSimpleDataPiece(
- const google::protobuf::Field& field, const google::protobuf::Type& type,
- const DataPiece& data) {
- // If we are rendering explicit null values and the backend proto field is not
- // of the google.protobuf.NullType type, we do nothing.
- if (data.type() == DataPiece::TYPE_NULL &&
- field.type_url() != kStructNullValueTypeUrl) {
- return;
- }
-
- // 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,
- ProtoElement::MESSAGE));
-
- // Make sure that field represents a simple data type.
- 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(),
- data.ValueAsStringOrDefault(""));
- return;
- }
-
- Status status;
- switch (field.kind()) {
- case google::protobuf::Field_Kind_TYPE_INT32: {
- status = WriteInt32(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_SFIXED32: {
- status = WriteSFixed32(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_SINT32: {
- status = WriteSInt32(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_FIXED32: {
- status = WriteFixed32(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_UINT32: {
- status = WriteUInt32(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_INT64: {
- status = WriteInt64(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_SFIXED64: {
- status = WriteSFixed64(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_SINT64: {
- status = WriteSInt64(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_FIXED64: {
- status = WriteFixed64(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_UINT64: {
- status = WriteUInt64(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_DOUBLE: {
- status = WriteDouble(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_FLOAT: {
- status = WriteFloat(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_BOOL: {
- status = WriteBool(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_BYTES: {
- status = WriteBytes(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_STRING: {
- 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());
- break;
- }
- default: // TYPE_GROUP or TYPE_MESSAGE
- status = Status(INVALID_ARGUMENT, data.ToString().ValueOrDie());
- }
- if (!status.ok()) {
- InvalidValue(google::protobuf::Field_Kind_Name(field.kind()),
- status.error_message());
- }
- element_.reset(element_->pop());
-}
-
// Map of functions that are responsible for rendering well known type
// represented by the key.
hash_map<string, ProtoStreamObjectWriter::TypeRenderer>*
@@ -1446,45 +1054,12 @@ ProtoStreamObjectWriter::FindTypeRenderer(const string& type_url) {
return FindOrNull(*renderers_, type_url);
}
-ProtoStreamObjectWriter::ProtoElement::ElementType
-ProtoStreamObjectWriter::GetElementType(const google::protobuf::Type& type) {
- if (type.name() == kAnyType) {
- return ProtoElement::ANY;
- } else if (type.name() == kStructType) {
- return ProtoElement::STRUCT;
- } else if (type.name() == kStructValueType) {
- return ProtoElement::STRUCT_VALUE;
- } else if (type.name() == kStructListValueType) {
- return ProtoElement::STRUCT_LIST_VALUE;
- } else {
- return ProtoElement::MESSAGE;
- }
-}
-
-bool ProtoStreamObjectWriter::ValidOneof(const google::protobuf::Field& field,
- StringPiece unnormalized_name) {
- if (element_ == NULL) return true;
-
- if (field.oneof_index() > 0) {
- if (element_->OneofIndexTaken(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 ProtoStreamObjectWriter::ValidMapKey(StringPiece unnormalized_name) {
- if (element_ == NULL) return true;
+ if (current_ == NULL) return true;
- if (!element_->InsertMapKeyIfNotPresent(unnormalized_name)) {
- InvalidName(
- unnormalized_name,
+ if (!current_->InsertMapKeyIfNotPresent(unnormalized_name)) {
+ listener()->InvalidName(
+ location(), unnormalized_name,
StrCat("Repeated map key: '", unnormalized_name, "' is already set."));
return false;
}
@@ -1492,134 +1067,30 @@ bool ProtoStreamObjectWriter::ValidMapKey(StringPiece unnormalized_name) {
return true;
}
-const google::protobuf::Field* ProtoStreamObjectWriter::BeginNamed(
- StringPiece name, bool is_list) {
- if (invalid_depth_ > 0) {
- ++invalid_depth_;
- return NULL;
- }
- const google::protobuf::Field* field = Lookup(name);
- if (field == NULL) {
- ++invalid_depth_;
- // InvalidName() already called in Lookup().
- return NULL;
- }
- if (is_list &&
- field->cardinality() !=
- google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
- ++invalid_depth_;
- InvalidName(name, "Proto field is not repeating, cannot start list.");
- return NULL;
- }
- return field;
-}
+void ProtoStreamObjectWriter::Push(StringPiece name, Item::ItemType item_type,
+ bool is_placeholder, bool is_list) {
+ is_list ? ProtoWriter::StartList(name) : ProtoWriter::StartObject(name);
-const google::protobuf::Field* ProtoStreamObjectWriter::Lookup(
- StringPiece unnormalized_name) {
- ProtoElement* e = element();
- if (e == NULL) {
- InvalidName(unnormalized_name, "Root element must be a message.");
- return NULL;
- }
- if (unnormalized_name.empty()) {
- // Objects in repeated field inherit the same field descriptor.
- if (e->field() == NULL) {
- InvalidName(unnormalized_name, "Proto fields must have a name.");
- } else if (e->field()->cardinality() !=
- google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
- InvalidName(unnormalized_name, "Proto fields must have a name.");
- return NULL;
- }
- return e->field();
- }
- const google::protobuf::Field* field =
- typeinfo_->FindField(&e->type(), unnormalized_name);
- if (field == NULL) InvalidName(unnormalized_name, "Cannot find field.");
- return field;
-}
-
-const google::protobuf::Type* ProtoStreamObjectWriter::LookupType(
- const google::protobuf::Field* field) {
- return (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE
- ? typeinfo_->GetTypeByTypeUrl(field->type_url())
- : &element_->type());
+ // invalid_depth == 0 means it is a successful StartObject or StartList.
+ if (invalid_depth() == 0)
+ current_.reset(
+ new Item(current_.release(), item_type, is_placeholder, is_list));
}
-// Looks up the oneof struct field based on the data type.
-StatusOr<const google::protobuf::Field*>
-ProtoStreamObjectWriter::LookupStructField(DataPiece::Type type) {
- const google::protobuf::Field* field = NULL;
- switch (type) {
- // Our JSON parser parses numbers as either int64, uint64, or double.
- case DataPiece::TYPE_INT64:
- case DataPiece::TYPE_UINT64:
- case DataPiece::TYPE_DOUBLE: {
- field = Lookup("number_value");
- break;
- }
- case DataPiece::TYPE_STRING: {
- field = Lookup("string_value");
- break;
- }
- case DataPiece::TYPE_BOOL: {
- field = Lookup("bool_value");
- break;
- }
- case DataPiece::TYPE_NULL: {
- field = Lookup("null_value");
- break;
- }
- default: { return Status(INVALID_ARGUMENT, "Invalid struct data type"); }
+void ProtoStreamObjectWriter::Pop() {
+ // Pop all placeholder items sending StartObject or StartList events to
+ // ProtoWriter according to is_list value.
+ while (current_ != NULL && current_->is_placeholder()) {
+ PopOneElement();
}
- if (field == NULL) {
- return Status(INVALID_ARGUMENT, "Could not lookup struct field");
+ if (current_ != NULL) {
+ PopOneElement();
}
- return field;
}
-void ProtoStreamObjectWriter::WriteRootMessage() {
- GOOGLE_DCHECK(!done_);
- int curr_pos = 0;
- // Calls the destructor of CodedOutputStream to remove any uninitialized
- // memory from the Cord before we read it.
- stream_.reset(NULL);
- const void* data;
- int length;
- google::protobuf::io::ArrayInputStream input_stream(buffer_.data(), buffer_.size());
- while (input_stream.Next(&data, &length)) {
- if (length == 0) continue;
- int num_bytes = length;
- // Write up to where we need to insert the size field.
- // The number of bytes we may write is the smaller of:
- // - the current fragment size
- // - the distance to the next position where a size field needs to be
- // inserted.
- if (!size_insert_.empty() &&
- size_insert_.front().pos - curr_pos < num_bytes) {
- num_bytes = size_insert_.front().pos - curr_pos;
- }
- output_->Append(static_cast<const char*>(data), num_bytes);
- if (num_bytes < length) {
- input_stream.BackUp(length - num_bytes);
- }
- curr_pos += num_bytes;
- // Insert the size field.
- // size_insert_.front(): the next <index, size> pair to be written.
- // size_insert_.front().pos: position of the size field.
- // size_insert_.front().size: the size (integer) to be inserted.
- if (!size_insert_.empty() && curr_pos == size_insert_.front().pos) {
- // Varint32 occupies at most 10 bytes.
- uint8 insert_buffer[10];
- uint8* insert_buffer_pos = CodedOutputStream::WriteVarint32ToArray(
- size_insert_.front().size, insert_buffer);
- output_->Append(reinterpret_cast<const char*>(insert_buffer),
- insert_buffer_pos - insert_buffer);
- size_insert_.pop_front();
- }
- }
- output_->Flush();
- stream_.reset(new CodedOutputStream(&adapter_));
- done_ = true;
+void ProtoStreamObjectWriter::PopOneElement() {
+ current_->is_list() ? ProtoWriter::EndList() : ProtoWriter::EndObject();
+ current_.reset(current_->pop<Item>());
}
bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) {
@@ -1630,7 +1101,7 @@ bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) {
return false;
}
const google::protobuf::Type* field_type =
- typeinfo_->GetTypeByTypeUrl(field.type_url());
+ typeinfo()->GetTypeByTypeUrl(field.type_url());
// TODO(xiaofeng): Unify option names.
return GetBoolOptionOrDefault(field_type->options(),
@@ -1638,12 +1109,23 @@ bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) {
GetBoolOptionOrDefault(field_type->options(), "map_entry", false);
}
-void ProtoStreamObjectWriter::WriteTag(const google::protobuf::Field& field) {
- WireFormatLite::WireType wire_type = WireFormatLite::WireTypeForFieldType(
- static_cast<WireFormatLite::FieldType>(field.kind()));
- stream_->WriteTag(WireFormatLite::MakeTag(field.number(), wire_type));
+bool ProtoStreamObjectWriter::IsAny(const google::protobuf::Field& field) {
+ return GetTypeWithoutUrl(field.type_url()) == kAnyType;
}
+bool ProtoStreamObjectWriter::IsStruct(const google::protobuf::Field& field) {
+ return GetTypeWithoutUrl(field.type_url()) == kStructType;
+}
+
+bool ProtoStreamObjectWriter::IsStructValue(
+ const google::protobuf::Field& field) {
+ return GetTypeWithoutUrl(field.type_url()) == kStructValueType;
+}
+
+bool ProtoStreamObjectWriter::IsStructListValue(
+ const google::protobuf::Field& field) {
+ return GetTypeWithoutUrl(field.type_url()) == kStructListValueType;
+}
} // namespace converter
} // namespace util
diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.h b/src/google/protobuf/util/internal/protostream_objectwriter.h
index 6e133679..08ac6e33 100644
--- a/src/google/protobuf/util/internal/protostream_objectwriter.h
+++ b/src/google/protobuf/util/internal/protostream_objectwriter.h
@@ -42,6 +42,7 @@
#include <google/protobuf/util/internal/type_info.h>
#include <google/protobuf/util/internal/datapiece.h>
#include <google/protobuf/util/internal/error_listener.h>
+#include <google/protobuf/util/internal/proto_writer.h>
#include <google/protobuf/util/internal/structured_objectwriter.h>
#include <google/protobuf/util/type_resolver.h>
#include <google/protobuf/stubs/bytestream.h>
@@ -67,9 +68,11 @@ namespace converter {
class ObjectLocationTracker;
// An ObjectWriter that can write protobuf bytes directly from writer events.
+// This class supports all special types like Struct and Map. It uses
+// the ProtoWriter class to write raw proto bytes.
//
// It also supports streaming.
-class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter {
+class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public ProtoWriter {
public:
// Constructor. Does not take ownership of any parameter passed in.
ProtoStreamObjectWriter(TypeResolver* type_resolver,
@@ -82,56 +85,13 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter
virtual ProtoStreamObjectWriter* EndObject();
virtual ProtoStreamObjectWriter* StartList(StringPiece name);
virtual ProtoStreamObjectWriter* EndList();
- virtual ProtoStreamObjectWriter* RenderBool(StringPiece name, bool value) {
- return RenderDataPiece(name, DataPiece(value));
- }
- virtual ProtoStreamObjectWriter* RenderInt32(StringPiece name, int32 value) {
- return RenderDataPiece(name, DataPiece(value));
- }
- virtual ProtoStreamObjectWriter* RenderUint32(StringPiece name,
- uint32 value) {
- return RenderDataPiece(name, DataPiece(value));
- }
- virtual ProtoStreamObjectWriter* RenderInt64(StringPiece name, int64 value) {
- return RenderDataPiece(name, DataPiece(value));
- }
- virtual ProtoStreamObjectWriter* RenderUint64(StringPiece name,
- uint64 value) {
- return RenderDataPiece(name, DataPiece(value));
- }
- virtual ProtoStreamObjectWriter* RenderDouble(StringPiece name,
- double value) {
- return RenderDataPiece(name, DataPiece(value));
- }
- virtual ProtoStreamObjectWriter* RenderFloat(StringPiece name, float value) {
- return RenderDataPiece(name, DataPiece(value));
- }
- virtual ProtoStreamObjectWriter* RenderString(StringPiece name,
- StringPiece value) {
- return RenderDataPiece(name, DataPiece(value));
- }
- virtual ProtoStreamObjectWriter* RenderBytes(StringPiece name,
- StringPiece value) {
- return RenderDataPiece(name, DataPiece(value, false));
- }
- virtual ProtoStreamObjectWriter* RenderNull(StringPiece name) {
- return RenderDataPiece(name, DataPiece::NullData());
- }
// Renders a DataPiece 'value' into a field whose wire type is determined
// from the given field 'name'.
- ProtoStreamObjectWriter* RenderDataPiece(StringPiece name,
- const DataPiece& value);
+ virtual ProtoStreamObjectWriter* RenderDataPiece(StringPiece name,
+ const DataPiece& value);
- // Returns the location tracker to use for tracking locations for errors.
- const LocationTrackerInterface& location() {
- return element_ != NULL ? *element_ : *tracker_;
- }
-
- // When true, we finished writing to output a complete message.
- bool done() const { return done_; }
-
- private:
+ protected:
// Function that renders a well known type with modified behavior.
typedef util::Status (*TypeRenderer)(ProtoStreamObjectWriter*,
const DataPiece&);
@@ -192,73 +152,37 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter
bool has_injected_value_message_;
};
- class LIBPROTOBUF_EXPORT ProtoElement : public BaseElement, public LocationTrackerInterface {
+ // Represents an item in a stack of items used to keep state between
+ // ObjectWrier events.
+ class LIBPROTOBUF_EXPORT Item : public BaseElement {
public:
- // Indicates the type of element. Special types like LIST, MAP, MAP_ENTRY,
- // STRUCT etc. are used to deduce other information based on their position
- // on the stack of elements.
- enum ElementType {
- MESSAGE, // Simple message
- LIST, // List/repeated element
- MAP, // Proto3 map type
- MAP_ENTRY, // Proto3 map message type, with 'key' and 'value' fields
- ANY, // Proto3 Any type
- STRUCT, // Proto3 struct type
- STRUCT_VALUE, // Struct's Value message type
- STRUCT_LIST, // List type indicator within a struct
- STRUCT_LIST_VALUE, // Struct Value's ListValue message type
- STRUCT_MAP, // Struct within a struct type
- STRUCT_MAP_ENTRY // Struct map's entry type with 'key' and 'value'
- // fields
+ // Indicates the type of item.
+ enum ItemType {
+ MESSAGE, // Simple message
+ MAP, // Proto3 map type
+ ANY, // Proto3 Any type
};
- // Constructor for the root element. No parent nor field.
- ProtoElement(const TypeInfo* typeinfo, const google::protobuf::Type& type,
- ProtoStreamObjectWriter* enclosing);
-
- // Constructor for a field of an element.
- ProtoElement(ProtoElement* parent, const google::protobuf::Field* field,
- const google::protobuf::Type& type, ElementType element_type);
+ // Constructor for the root item.
+ Item(ProtoStreamObjectWriter* enclosing, ItemType item_type,
+ bool is_placeholder, bool is_list);
- virtual ~ProtoElement() {}
+ // Constructor for a field of a message.
+ Item(Item* parent, ItemType item_type, bool is_placeholder, bool is_list);
- // Called just before the destructor for clean up:
- // - reports any missing required fields
- // - computes the space needed by the size field, and augment the
- // length of all parent messages by this additional space.
- // - releases and returns the parent pointer.
- ProtoElement* pop();
-
- // Accessors
- const google::protobuf::Field* field() const { return field_; }
- const google::protobuf::Type& type() const { return type_; }
+ virtual ~Item() {}
// These functions return true if the element type is corresponding to the
// type in function name.
- bool IsMap() { return element_type_ == MAP; }
- bool IsStructMap() { return element_type_ == STRUCT_MAP; }
- bool IsStructMapEntry() { return element_type_ == STRUCT_MAP_ENTRY; }
- bool IsStructList() { return element_type_ == STRUCT_LIST; }
- bool IsAny() { return element_type_ == ANY; }
-
- ElementType element_type() { return element_type_; }
-
- void RegisterField(const google::protobuf::Field* field);
- virtual string ToString() const;
+ bool IsMap() { return item_type_ == MAP; }
+ bool IsAny() { return item_type_ == ANY; }
AnyWriter* any() const { return any_.get(); }
- virtual ProtoElement* parent() const {
- return static_cast<ProtoElement*>(BaseElement::parent());
+ virtual Item* parent() const {
+ return static_cast<Item*>(BaseElement::parent());
}
- // Returns true if the index is already taken by a preceeding oneof input.
- bool OneofIndexTaken(int32 index);
-
- // Marks the oneof 'index' as taken. Future inputs to this oneof will
- // generate an error.
- void TakeOneofIndex(int32 index);
-
// Inserts map key into hash set if and only if the key did NOT already
// exist in hash set.
// The hash set (map_keys_) is ONLY used to keep track of map keys.
@@ -266,6 +190,9 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter
// already present.
bool InsertMapKeyIfNotPresent(StringPiece map_key);
+ bool is_placeholder() const { return is_placeholder_; }
+ bool is_list() const { return is_list_; }
+
private:
// Used for access to variables of the enclosing instance of
// ProtoStreamObjectWriter.
@@ -274,126 +201,42 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter
// A writer for Any objects, handles all Any-related nonsense.
google::protobuf::scoped_ptr<AnyWriter> any_;
- // Describes the element as a field in the parent message.
- // field_ is NULL if and only if this element is the root element.
- const google::protobuf::Field* field_;
-
- // TypeInfo to lookup types.
- const TypeInfo* typeinfo_;
-
- // Additional variables if this element is a message:
- // (Root element is always a message).
- // descriptor_ : describes allowed fields in the message.
- // required_fields_: set of required fields.
- // is_repeated_type_ : true if the element is of type list or map.
- // size_index_ : index into ProtoStreamObjectWriter::size_insert_
- // for later insertion of serialized message length.
- const google::protobuf::Type& type_;
- std::set<const google::protobuf::Field*> required_fields_;
- const bool is_repeated_type_;
- const int size_index_;
-
- // Tracks position in repeated fields, needed for LocationTrackerInterface.
- int array_index_;
-
// The type of this element, see enum for permissible types.
- ElementType element_type_;
-
- // Set of oneof indices already seen for the type_. Used to validate
- // incoming messages so no more than one oneof is set.
- hash_set<int32> oneof_indices_;
+ ItemType item_type_;
// 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_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoElement);
- };
+ // Conveys whether this Item is a placeholder or not. Placeholder items are
+ // pushed to stack to account for special types.
+ bool is_placeholder_;
- // Container for inserting 'size' information at the 'pos' position.
- struct SizeInfo {
- const int pos;
- int size;
+ // Conveys whether this Item is a list or not. This is used to send
+ // StartList or EndList calls to underlying ObjectWriter.
+ bool is_list_;
+
+ GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(Item);
};
ProtoStreamObjectWriter(const TypeInfo* typeinfo,
const google::protobuf::Type& type,
strings::ByteSink* output, ErrorListener* listener);
- ProtoElement* element() { return element_.get(); }
-
- // Helper methods for calling ErrorListener. See error_listener.h.
- void InvalidName(StringPiece unknown_name, StringPiece message);
- void InvalidValue(StringPiece type_name, StringPiece value);
- void MissingField(StringPiece missing_name);
-
- // Common code for BeginObject() and BeginList() that does invalid_depth_
- // bookkeeping associated with name lookup.
- const google::protobuf::Field* BeginNamed(StringPiece name, bool is_list);
-
- // Lookup the field in the current element. Looks in the base descriptor
- // and in any extension. This will report an error if the field cannot be
- // found or if multiple matching extensions are found.
- const google::protobuf::Field* Lookup(StringPiece name);
-
- // Lookup the field type in the type descriptor. Returns NULL if the type
- // is not known.
- const google::protobuf::Type* LookupType(
- const google::protobuf::Field* field);
-
- // Looks up the oneof struct Value field depending on the type.
- // On failure to find, it returns an appropriate error.
- util::StatusOr<const google::protobuf::Field*> LookupStructField(
- DataPiece::Type type);
-
- // Starts an entry in map. This will be called after placing map element at
- // the top of the stack. Uses this information to write map entries.
- const google::protobuf::Field* StartMapEntry(StringPiece name);
-
- // Starts a google.protobuf.Struct.
- // 'field' is of type google.protobuf.Struct.
- // If field is NULL, it indicates that the top-level message is a struct
- // type.
- void StartStruct(const google::protobuf::Field* field);
-
- // Starts another struct within a struct.
- // 'field' is of type google.protobuf.Value (see struct.proto).
- const google::protobuf::Field* StartStructValueInStruct(
- const google::protobuf::Field* field);
-
- // Starts a list within a struct.
- // 'field' is of type google.protobuf.ListValue (see struct.proto).
- const google::protobuf::Field* StartListValueInStruct(
- const google::protobuf::Field* field);
-
- // Starts the repeated "values" field in struct.proto's
- // google.protobuf.ListValue type. 'field' should be of type
- // google.protobuf.ListValue.
- const google::protobuf::Field* StartRepeatedValuesInListValue(
- const google::protobuf::Field* field);
-
- // Pops sentinel elements off the stack.
- void SkipElements();
-
- // Write serialized output to the final output ByteSink, inserting all
- // the size information for nested messages that are missing from the
- // intermediate Cord buffer.
- void WriteRootMessage();
-
// Returns true if the field is a map.
bool IsMap(const google::protobuf::Field& field);
// Returns true if the field is an any.
bool IsAny(const google::protobuf::Field& field);
- // Helper method to write proto tags based on the given field.
- void WriteTag(const google::protobuf::Field& field);
+ // Returns true if the field is google.protobuf.Struct.
+ bool IsStruct(const google::protobuf::Field& field);
+ // Returns true if the field is google.protobuf.Value.
+ bool IsStructValue(const google::protobuf::Field& field);
- // Helper function to render primitive data types in DataPiece.
- void RenderSimpleDataPiece(const google::protobuf::Field& field,
- const google::protobuf::Type& type,
- const DataPiece& data);
+ // Returns true if the field is google.protobuf.ListValue.
+ 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.
@@ -417,69 +260,46 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter
static util::Status RenderWrapperType(ProtoStreamObjectWriter* ow,
const DataPiece& value);
- // Helper functions to create the map and find functions responsible for
- // rendering well known types, keyed by type URL.
- static hash_map<string, TypeRenderer>* renderers_;
static void InitRendererMap();
static void DeleteRendererMap();
static TypeRenderer* FindTypeRenderer(const string& type_url);
- // Returns the ProtoElement::ElementType for the given Type.
- static ProtoElement::ElementType GetElementType(
- const google::protobuf::Type& type);
-
- // Returns true if the field for type_ can be set as a oneof. If field is not
- // a oneof type, this function does nothing and returns true.
- // If another field for this oneof is already set, this function returns
- // false. It also calls the appropriate error callback.
- // unnormalized_name is used for error string.
- bool ValidOneof(const google::protobuf::Field& field,
- StringPiece unnormalized_name);
-
// Returns true if the map key for type_ is not duplicated key.
// If map key is duplicated key, this function returns false.
- // Note that caller should make sure that the current proto element (element_)
+ // Note that caller should make sure that the current proto element (current_)
// is of element type MAP or STRUCT_MAP.
// It also calls the appropriate error callback and unnormalzied_name is used
// for error string.
bool ValidMapKey(StringPiece unnormalized_name);
+ // Pushes an item on to the stack. Also calls either StartObject or StartList
+ // on the underlying ObjectWriter depending on whether is_list is false or
+ // not.
+ // is_placeholder conveys whether the item is a placeholder item or not.
+ // Placeholder items are pushed when adding auxillary types' StartObject or
+ // StartList calls.
+ void Push(StringPiece name, Item::ItemType item_type, bool is_placeholder,
+ bool is_list);
+
+ // Pops items from the stack. All placeholder items are popped until a
+ // non-placeholder item is found.
+ void Pop();
+
+ // Pops one element from the stack. Calls EndObject() or EndList() on the
+ // underlying ObjectWriter depending on the value of is_list_.
+ void PopOneElement();
+
+ private:
+ // Helper functions to create the map and find functions responsible for
+ // rendering well known types, keyed by type URL.
+ static hash_map<string, TypeRenderer>* renderers_;
+
// Variables for describing the structure of the input tree:
// master_type_: descriptor for the whole protobuf message.
- // typeinfo_ : the TypeInfo object to lookup types.
const google::protobuf::Type& master_type_;
- const TypeInfo* typeinfo_;
- // Whether we own the typeinfo_ object.
- bool own_typeinfo_;
-
- // Indicates whether we finished writing root message completely.
- bool done_;
-
- // Variable for internal state processing:
- // element_ : the current element.
- // size_insert_: sizes of nested messages.
- // pos - position to insert the size field.
- // size - size value to be inserted.
- google::protobuf::scoped_ptr<ProtoElement> element_;
- std::deque<SizeInfo> size_insert_;
-
- // Variables for output generation:
- // output_ : pointer to an external ByteSink for final user-visible output.
- // buffer_ : buffer holding partial message before being ready for output_.
- // adapter_ : internal adapter between CodedOutputStream and Cord buffer_.
- // stream_ : wrapper for writing tags and other encodings in wire format.
- strings::ByteSink* output_;
- string buffer_;
- google::protobuf::io::StringOutputStream adapter_;
- google::protobuf::scoped_ptr<google::protobuf::io::CodedOutputStream> stream_;
-
- // Variables for error tracking and reporting:
- // listener_ : a place to report any errors found.
- // invalid_depth_: number of enclosing invalid nested messages.
- // tracker_ : the root location tracker interface.
- ErrorListener* listener_;
- int invalid_depth_;
- google::protobuf::scoped_ptr<LocationTrackerInterface> tracker_;
+
+ // The current element, variable for internal state processing.
+ google::protobuf::scoped_ptr<Item> current_;
GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoStreamObjectWriter);
};
diff --git a/src/google/protobuf/util/internal/protostream_objectwriter_test.cc b/src/google/protobuf/util/internal/protostream_objectwriter_test.cc
index cb9f97f7..c7667c21 100644
--- a/src/google/protobuf/util/internal/protostream_objectwriter_test.cc
+++ b/src/google/protobuf/util/internal/protostream_objectwriter_test.cc
@@ -241,6 +241,21 @@ TEST_P(ProtoStreamObjectWriterTest, SimpleMessage) {
CheckOutput(book);
}
+TEST_P(ProtoStreamObjectWriterTest, CustomJsonName) {
+ Book book;
+ Author* robert = book.mutable_author();
+ robert->set_id(12345);
+ robert->set_name("robert");
+
+ ow_->StartObject("")
+ ->StartObject("author")
+ ->RenderUint64("@id", 12345)
+ ->RenderString("name", "robert")
+ ->EndObject()
+ ->EndObject();
+ CheckOutput(book);
+}
+
TEST_P(ProtoStreamObjectWriterTest, PrimitiveFromStringConversion) {
Primitive full;
full.set_fix32(101);
@@ -796,10 +811,6 @@ TEST_P(ProtoStreamObjectWriterTest, RootNamedList) {
InvalidName(_, StringPiece("oops"),
StringPiece("Root element should not be named.")))
.With(Args<0>(HasObjectLocation("")));
- EXPECT_CALL(listener_,
- InvalidName(_, StringPiece(""),
- StringPiece("Proto fields must have a name.")))
- .With(Args<0>(HasObjectLocation("")));
ow_->StartList("oops")->RenderString("", "item")->EndList();
CheckOutput(empty, 0);
}
@@ -864,6 +875,18 @@ INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest,
::testing::Values(
testing::USE_TYPE_RESOLVER));
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, ParseTimestamp) {
+ TimestampDuration timestamp;
+ google::protobuf::Timestamp* ts = timestamp.mutable_ts();
+ ts->set_seconds(1448249855);
+ ts->set_nanos(33155000);
+
+ ow_->StartObject("")
+ ->RenderString("ts", "2015-11-23T03:37:35.033155Z")
+ ->EndObject();
+ CheckOutput(timestamp);
+}
+
TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError1) {
TimestampDuration timestamp;
@@ -922,9 +945,66 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError4) {
CheckOutput(timestamp);
}
-// TODO(skarvaje): Write a test for nanos that exceed limit. Currently, it is
-// not possible to construct a test case where nanos exceed limit because of
-// floating point arithmetic.
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError5) {
+ TimestampDuration timestamp;
+
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_,
+ StringPiece("type.googleapis.com/google.protobuf.Timestamp"),
+ StringPiece("Field 'ts', Invalid time format: "
+ "2015-11-23T03:37:35.033155 Z")));
+
+ ow_->StartObject("")
+ // Whitespace in the Timestamp nanos is not allowed.
+ ->RenderString("ts", "2015-11-23T03:37:35.033155 Z")
+ ->EndObject();
+ CheckOutput(timestamp);
+}
+
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError6) {
+ TimestampDuration timestamp;
+
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_,
+ StringPiece("type.googleapis.com/google.protobuf.Timestamp"),
+ StringPiece("Field 'ts', Invalid time format: "
+ "2015-11-23T03:37:35.033155 1234Z")));
+
+ ow_->StartObject("")
+ // Whitespace in the Timestamp nanos is not allowed.
+ ->RenderString("ts", "2015-11-23T03:37:35.033155 1234Z")
+ ->EndObject();
+ CheckOutput(timestamp);
+}
+
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError7) {
+ TimestampDuration timestamp;
+
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_,
+ StringPiece("type.googleapis.com/google.protobuf.Timestamp"),
+ StringPiece("Field 'ts', Invalid time format: "
+ "2015-11-23T03:37:35.033abc155Z")));
+
+ ow_->StartObject("")
+ // Non-numeric characters in the Timestamp nanos is not allowed.
+ ->RenderString("ts", "2015-11-23T03:37:35.033abc155Z")
+ ->EndObject();
+ CheckOutput(timestamp);
+}
+
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, ParseDuration) {
+ TimestampDuration duration;
+ google::protobuf::Duration* dur = duration.mutable_dur();
+ dur->set_seconds(1448216930);
+ dur->set_nanos(132262000);
+
+ ow_->StartObject("")->RenderString("dur", "1448216930.132262s")->EndObject();
+ CheckOutput(duration);
+}
TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError1) {
TimestampDuration duration;
@@ -962,7 +1042,7 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError3) {
InvalidValue(
_, StringPiece("type.googleapis.com/google.protobuf.Duration"),
StringPiece("Field 'dur', Invalid duration format, failed to "
- "parse nanos seconds")));
+ "parse nano seconds")));
ow_->StartObject("")->RenderString("dur", "123.DEFs")->EndObject();
CheckOutput(duration);
@@ -981,6 +1061,19 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError4) {
CheckOutput(duration);
}
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError5) {
+ TimestampDuration duration;
+
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_,
+ StringPiece("type.googleapis.com/google.protobuf.Duration"),
+ StringPiece("Field 'dur', Duration value exceeds limits")));
+
+ ow_->StartObject("")->RenderString("dur", "0.1000000001s")->EndObject();
+ CheckOutput(duration);
+}
+
TEST_P(ProtoStreamObjectWriterTimestampDurationTest,
MismatchedTimestampTypeInput) {
TimestampDuration timestamp;
@@ -1066,12 +1159,12 @@ TEST_P(ProtoStreamObjectWriterStructTest, StructInvalidInputFailure) {
TEST_P(ProtoStreamObjectWriterStructTest, SimpleRepeatedStructMapKeyTest) {
EXPECT_CALL(
listener_,
- InvalidName(_, StringPiece("k1"),
- StringPiece("Repeated map key: 'k1' is already set.")));
+ InvalidName(_, StringPiece("gBike"),
+ StringPiece("Repeated map key: 'gBike' is already set.")));
ow_->StartObject("")
->StartObject("object")
- ->RenderString("k1", "v1")
- ->RenderString("k1", "v2")
+ ->RenderString("gBike", "v1")
+ ->RenderString("gBike", "v2")
->EndObject()
->EndObject();
}
@@ -1121,10 +1214,11 @@ INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest,
TEST_P(ProtoStreamObjectWriterMapTest, MapShouldNotAcceptList) {
MapIn mm;
- EXPECT_CALL(listener_,
- InvalidValue(_, StringPiece("Map"),
- StringPiece("Cannot bind a list to map.")))
- .With(Args<0>(HasObjectLocation("map_input")));
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(
+ _, StringPiece("Map"),
+ StringPiece("Cannot bind a list to map for field 'map_input'.")));
ow_->StartObject("")
->StartList("map_input")
->RenderString("a", "b")
diff --git a/src/google/protobuf/util/internal/testdata/books.proto b/src/google/protobuf/util/internal/testdata/books.proto
index 6e2f109b..82b81760 100644
--- a/src/google/protobuf/util/internal/testdata/books.proto
+++ b/src/google/protobuf/util/internal/testdata/books.proto
@@ -66,7 +66,7 @@ message Publisher {
// An author of a book
message Author {
- optional uint64 id = 1;
+ optional uint64 id = 1 [json_name = "@id"];
optional string name = 2;
repeated string pseudonym = 3;
optional bool alive = 4;
diff --git a/src/google/protobuf/util/internal/testdata/default_value.proto b/src/google/protobuf/util/internal/testdata/default_value.proto
index ebbdf6ab..cccc741c 100644
--- a/src/google/protobuf/util/internal/testdata/default_value.proto
+++ b/src/google/protobuf/util/internal/testdata/default_value.proto
@@ -75,9 +75,10 @@ message DefaultValueTestCases {
IntToStringMap int_to_string = 403;
MixedMap mixed1 = 404;
MixedMap2 mixed2 = 405;
- MessageMap map_of_objects = 406;
- MixedMap mixed_empty = 407;
- MessageMap message_map_empty = 408;
+ MixedMap2 empty_mixed2 = 406;
+ MessageMap map_of_objects = 407;
+ MixedMap mixed_empty = 408;
+ MessageMap message_map_empty = 409;
DoubleValueMessage double_value = 501;
DoubleValueMessage double_value_default = 502;
}
diff --git a/src/google/protobuf/util/internal/testdata/default_value_test.proto b/src/google/protobuf/util/internal/testdata/default_value_test.proto
index 21b85e6d..93288341 100644
--- a/src/google/protobuf/util/internal/testdata/default_value_test.proto
+++ b/src/google/protobuf/util/internal/testdata/default_value_test.proto
@@ -43,4 +43,11 @@ message DefaultValueTest {
bool bool_value = 13;
string string_value = 15;
bytes bytes_value = 17 [ctype = CORD];
+
+ enum EnumDefault {
+ ENUM_FIRST = 0;
+ ENUM_SECOND = 1;
+ ENUM_THIRD = 2;
+ }
+ EnumDefault enum_value = 18;
}
diff --git a/src/google/protobuf/util/internal/type_info.cc b/src/google/protobuf/util/internal/type_info.cc
index a45a76e3..00a8ee7a 100644
--- a/src/google/protobuf/util/internal/type_info.cc
+++ b/src/google/protobuf/util/internal/type_info.cc
@@ -136,8 +136,7 @@ class TypeInfoForTypeResolver : public TypeInfo {
for (int i = 0; i < type->fields_size(); ++i) {
const google::protobuf::Field& field = type->fields(i);
StringPiece name = field.name();
- StringPiece camel_case_name =
- *string_storage_.insert(ToCamelCase(name)).first;
+ StringPiece camel_case_name = field.json_name();
const StringPiece* existing = InsertOrReturnExisting(
&camel_case_name_table_, camel_case_name, name);
if (existing && *existing != name) {
diff --git a/src/google/protobuf/util/internal/utility.cc b/src/google/protobuf/util/internal/utility.cc
index 61899c24..1ddf2487 100644
--- a/src/google/protobuf/util/internal/utility.cc
+++ b/src/google/protobuf/util/internal/utility.cc
@@ -49,7 +49,8 @@ namespace converter {
namespace {
const StringPiece SkipWhiteSpace(StringPiece str) {
StringPiece::size_type i;
- for (i = 0; i < str.size() && isspace(str[i]); ++i) {}
+ for (i = 0; i < str.size() && isspace(str[i]); ++i) {
+ }
GOOGLE_DCHECK(i == str.size() || !isspace(str[i]));
return StringPiece(str, i);
}
@@ -160,6 +161,19 @@ const google::protobuf::Field* FindFieldInTypeOrNull(
return NULL;
}
+const google::protobuf::Field* FindJsonFieldInTypeOrNull(
+ const google::protobuf::Type* type, StringPiece json_name) {
+ if (type != NULL) {
+ for (int i = 0; i < type->fields_size(); ++i) {
+ const google::protobuf::Field& field = type->fields(i);
+ if (field.json_name() == json_name) {
+ return &field;
+ }
+ }
+ }
+ return NULL;
+}
+
const google::protobuf::EnumValue* FindEnumValueByNameOrNull(
const google::protobuf::Enum* enum_type, StringPiece enum_name) {
if (enum_type != NULL) {
@@ -316,16 +330,23 @@ string FloatAsString(float value) {
return DoubleAsString(value);
}
-bool SafeStrToFloat(StringPiece str, float *value) {
+bool SafeStrToFloat(StringPiece str, float* value) {
double double_value;
if (!safe_strtod(str, &double_value)) {
return false;
}
- *value = static_cast<float>(double_value);
- if (MathLimits<float>::IsInf(*value)) {
+ if (MathLimits<double>::IsInf(double_value) ||
+ MathLimits<double>::IsNaN(double_value))
+ return false;
+
+ // Fail if the value is not representable in float.
+ if (double_value > std::numeric_limits<float>::max() ||
+ double_value < -std::numeric_limits<float>::max()) {
return false;
}
+
+ *value = static_cast<float>(double_value);
return true;
}
diff --git a/src/google/protobuf/util/internal/utility.h b/src/google/protobuf/util/internal/utility.h
index 5ba97bd2..33df8eda 100644
--- a/src/google/protobuf/util/internal/utility.h
+++ b/src/google/protobuf/util/internal/utility.h
@@ -127,6 +127,11 @@ const google::protobuf::Option* FindOptionOrNull(
const google::protobuf::Field* FindFieldInTypeOrNull(
const google::protobuf::Type* type, StringPiece field_name);
+// Similar to FindFieldInTypeOrNull, but this looks up fields with given
+// json_name.
+const google::protobuf::Field* FindJsonFieldInTypeOrNull(
+ const google::protobuf::Type* type, StringPiece json_name);
+
// Finds and returns the EnumValue identified by enum_name in the passed tech
// Enum object. Returns NULL if none found.
const google::protobuf::EnumValue* FindEnumValueByNameOrNull(
diff --git a/src/google/protobuf/util/json_format_proto3.proto b/src/google/protobuf/util/json_format_proto3.proto
index e8137677..a1e24c18 100644
--- a/src/google/protobuf/util/json_format_proto3.proto
+++ b/src/google/protobuf/util/json_format_proto3.proto
@@ -165,3 +165,12 @@ message TestListValue {
google.protobuf.ListValue value = 1;
repeated google.protobuf.ListValue repeated_value = 2;
}
+
+message TestBoolValue {
+ bool bool_value = 1;
+ map<bool, int32> bool_map = 2;
+}
+
+message TestCustomJsonName {
+ int32 value = 1 [json_name = "@value"];
+}
diff --git a/src/google/protobuf/util/message_differencer.cc b/src/google/protobuf/util/message_differencer.cc
index 47237e5a..0f879dc7 100644
--- a/src/google/protobuf/util/message_differencer.cc
+++ b/src/google/protobuf/util/message_differencer.cc
@@ -238,9 +238,25 @@ void MessageDifferencer::TreatAsSet(const FieldDescriptor* field) {
GOOGLE_CHECK(key_comparator == NULL)
<< "Cannot treat this repeated field as both Map and Set for"
<< " comparison. Field name is: " << field->full_name();
+ GOOGLE_CHECK(list_fields_.find(field) == list_fields_.end())
+ << "Cannot treat the same field as both SET and LIST. Field name is: "
+ << field->full_name();
set_fields_.insert(field);
}
+void MessageDifferencer::TreatAsList(const FieldDescriptor* field) {
+ GOOGLE_CHECK(field->is_repeated()) << "Field must be repeated: "
+ << field->full_name();
+ const MapKeyComparator* key_comparator = GetMapKeyComparator(field);
+ GOOGLE_CHECK(key_comparator == NULL)
+ << "Cannot treat this repeated field as both Map and Set for"
+ << " comparison. Field name is: " << field->full_name();
+ GOOGLE_CHECK(set_fields_.find(field) == set_fields_.end())
+ << "Cannot treat the same field as both SET and LIST. Field name is: "
+ << field->full_name();
+ list_fields_.insert(field);
+}
+
void MessageDifferencer::TreatAsMap(const FieldDescriptor* field,
const FieldDescriptor* key) {
GOOGLE_CHECK(field->is_repeated()) << "Field must be repeated: "
@@ -255,6 +271,9 @@ void MessageDifferencer::TreatAsMap(const FieldDescriptor* field,
GOOGLE_CHECK(set_fields_.find(field) == set_fields_.end())
<< "Cannot treat this repeated field as both Map and Set for "
<< "comparison.";
+ GOOGLE_CHECK(list_fields_.find(field) == list_fields_.end())
+ << "Cannot treat this repeated field as both Map and List for "
+ << "comparison.";
MapKeyComparator* key_comparator =
new MultipleFieldsMapKeyComparator(this, key);
owned_key_comparators_.push_back(key_comparator);
@@ -920,7 +939,8 @@ bool MessageDifferencer::CheckPathChanged(
bool MessageDifferencer::IsTreatedAsSet(const FieldDescriptor* field) {
if (!field->is_repeated()) return false;
if (field->is_map()) return true;
- if (repeated_field_comparison_ == AS_SET) return true;
+ if (repeated_field_comparison_ == AS_SET)
+ return list_fields_.find(field) == list_fields_.end();
return (set_fields_.find(field) != set_fields_.end());
}
diff --git a/src/google/protobuf/util/message_differencer.h b/src/google/protobuf/util/message_differencer.h
index 34c173db..3ea74e67 100644
--- a/src/google/protobuf/util/message_differencer.h
+++ b/src/google/protobuf/util/message_differencer.h
@@ -397,9 +397,16 @@ class LIBPROTOBUF_EXPORT MessageDifferencer {
// + n^3) in which n^3 is the time complexity of the maximum matching
// algorithm.
//
- // REQUIRES: field->is_repeated()
+ // REQUIRES: field->is_repeated() and field not registered with TreatAsList
void TreatAsSet(const FieldDescriptor* field);
+ // The elements of the given repeated field will be treated as a list for
+ // diffing purposes, so different orderings of the same elements will NOT be
+ // considered equal.
+ //
+ // REQUIRED: field->is_repeated() and field not registered with TreatAsSet
+ void TreatAsList(const FieldDescriptor* field);
+
// The elements of the given repeated field will be treated as a map for
// diffing purposes, with |key| being the map key. Thus, elements with the
// same key will be compared even if they do not appear at the same index.
@@ -791,6 +798,7 @@ class LIBPROTOBUF_EXPORT MessageDifferencer {
RepeatedFieldComparison repeated_field_comparison_;
FieldSet set_fields_;
+ FieldSet list_fields_;
// Keeps track of MapKeyComparators that are created within
// MessageDifferencer. These MapKeyComparators should be deleted
// before MessageDifferencer is destroyed.
diff --git a/src/google/protobuf/util/message_differencer_unittest.cc b/src/google/protobuf/util/message_differencer_unittest.cc
index 701b94ae..4e9ca348 100755
--- a/src/google/protobuf/util/message_differencer_unittest.cc
+++ b/src/google/protobuf/util/message_differencer_unittest.cc
@@ -1103,12 +1103,19 @@ TEST(MessageDifferencerTest, RepeatedFieldSetTest_Combination) {
msg1.add_rw("change"); msg2.add_rw("change");
// Compare
- util::MessageDifferencer differencer;
- differencer.TreatAsMap(msg1.GetDescriptor()->FindFieldByName("item"),
- item->GetDescriptor()->FindFieldByName("a"));
- differencer.TreatAsSet(msg1.GetDescriptor()->FindFieldByName("rv"));
- differencer.TreatAsSet(item->GetDescriptor()->FindFieldByName("ra"));
- EXPECT_TRUE(differencer.Compare(msg1, msg2));
+ util::MessageDifferencer differencer1;
+ differencer1.TreatAsMap(msg1.GetDescriptor()->FindFieldByName("item"),
+ item->GetDescriptor()->FindFieldByName("a"));
+ differencer1.TreatAsSet(msg1.GetDescriptor()->FindFieldByName("rv"));
+ differencer1.TreatAsSet(item->GetDescriptor()->FindFieldByName("ra"));
+ EXPECT_TRUE(differencer1.Compare(msg1, msg2));
+
+ util::MessageDifferencer differencer2;
+ differencer2.TreatAsMap(msg1.GetDescriptor()->FindFieldByName("item"),
+ item->GetDescriptor()->FindFieldByName("a"));
+ differencer2.set_repeated_field_comparison(util::MessageDifferencer::AS_SET);
+ differencer2.TreatAsList(msg1.GetDescriptor()->FindFieldByName("rw"));
+ EXPECT_TRUE(differencer2.Compare(msg1, msg2));
}
TEST(MessageDifferencerTest, RepeatedFieldMapTest_Partial) {
@@ -1168,6 +1175,11 @@ TEST(MessageDifferencerTest, RepeatedFieldSetTest_Duplicates) {
differencer.TreatAsSet(GetFieldDescriptor(a, "rv"));
EXPECT_TRUE(differencer.Compare(b, a));
EXPECT_FALSE(differencer.Compare(c, a));
+
+ util::MessageDifferencer differencer1;
+ differencer1.set_repeated_field_comparison(util::MessageDifferencer::AS_SET);
+ EXPECT_TRUE(differencer1.Compare(b, a));
+ EXPECT_FALSE(differencer1.Compare(c, a));
}
TEST(MessageDifferencerTest, RepeatedFieldSetTest_PartialSimple) {
diff --git a/src/google/protobuf/util/type_resolver_util.cc b/src/google/protobuf/util/type_resolver_util.cc
index a0996954..96393903 100644
--- a/src/google/protobuf/util/type_resolver_util.cc
+++ b/src/google/protobuf/util/type_resolver_util.cc
@@ -159,7 +159,10 @@ class DescriptorPoolTypeResolver : public TypeResolver {
}
field->set_number(descriptor->number());
field->set_name(descriptor->name());
- field->set_json_name(converter::ToCamelCase(descriptor->name()));
+ field->set_json_name(descriptor->json_name());
+ if (descriptor->has_default_value()) {
+ field->set_default_value(DefaultValueAsString(descriptor));
+ }
if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE) {
field->set_type_url(GetTypeUrl(descriptor->message_type()));
} else if (descriptor->type() == FieldDescriptor::TYPE_ENUM) {
@@ -200,6 +203,46 @@ class DescriptorPoolTypeResolver : public TypeResolver {
return url_prefix_ + "/" + descriptor->full_name();
}
+ string DefaultValueAsString(const FieldDescriptor* descriptor) {
+ switch (descriptor->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32:
+ return SimpleItoa(descriptor->default_value_int32());
+ break;
+ case FieldDescriptor::CPPTYPE_INT64:
+ return SimpleItoa(descriptor->default_value_int64());
+ break;
+ case FieldDescriptor::CPPTYPE_UINT32:
+ return SimpleItoa(descriptor->default_value_uint32());
+ break;
+ case FieldDescriptor::CPPTYPE_UINT64:
+ return SimpleItoa(descriptor->default_value_uint64());
+ break;
+ case FieldDescriptor::CPPTYPE_FLOAT:
+ return SimpleFtoa(descriptor->default_value_float());
+ break;
+ case FieldDescriptor::CPPTYPE_DOUBLE:
+ return SimpleDtoa(descriptor->default_value_double());
+ break;
+ case FieldDescriptor::CPPTYPE_BOOL:
+ return descriptor->default_value_bool() ? "true" : "false";
+ break;
+ case FieldDescriptor::CPPTYPE_STRING:
+ if (descriptor->type() == FieldDescriptor::TYPE_BYTES) {
+ return CEscape(descriptor->default_value_string());
+ } else {
+ return descriptor->default_value_string();
+ }
+ break;
+ case FieldDescriptor::CPPTYPE_ENUM:
+ return descriptor->default_value_enum()->name();
+ break;
+ case FieldDescriptor::CPPTYPE_MESSAGE:
+ GOOGLE_LOG(DFATAL) << "Messages can't have default values!";
+ break;
+ }
+ return "";
+ }
+
string url_prefix_;
const DescriptorPool* pool_;
};
diff --git a/src/google/protobuf/util/type_resolver_util_test.cc b/src/google/protobuf/util/type_resolver_util_test.cc
index 74b2d0da..8a0bf652 100644
--- a/src/google/protobuf/util/type_resolver_util_test.cc
+++ b/src/google/protobuf/util/type_resolver_util_test.cc
@@ -43,6 +43,7 @@
#include <google/protobuf/map_unittest.pb.h>
#include <google/protobuf/test_util.h>
#include <google/protobuf/unittest.pb.h>
+#include <google/protobuf/util/json_format_proto3.pb.h>
#include <google/protobuf/util/type_resolver.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
@@ -332,6 +333,19 @@ TEST_F(DescriptorPoolTypeResolverTest, TestEnum) {
EnumHasValue(type, "NEG", -1);
}
+TEST_F(DescriptorPoolTypeResolverTest, TestJsonName) {
+ Type type;
+ ASSERT_TRUE(resolver_->ResolveMessageType(
+ GetTypeUrl<protobuf_unittest::TestAllTypes>(), &type)
+ .ok());
+ EXPECT_EQ("optionalInt32", FindField(type, "optional_int32")->json_name());
+
+ ASSERT_TRUE(resolver_->ResolveMessageType(
+ GetTypeUrl<proto3::TestCustomJsonName>(), &type)
+ .ok());
+ EXPECT_EQ("@value", FindField(type, "value")->json_name());
+}
+
} // namespace
} // namespace util
} // namespace protobuf