diff options
-rw-r--r-- | BUILD | 19 | ||||
-rw-r--r-- | benchmarks/README.md | 15 | ||||
-rw-r--r-- | conformance/failure_list_ruby.txt | 2 | ||||
-rw-r--r-- | docs/performance.md | 304 | ||||
-rw-r--r-- | src/google/protobuf/compiler/php/php_generator.h | 9 | ||||
-rw-r--r-- | src/google/protobuf/generated_message_util.h | 2 | ||||
-rw-r--r-- | src/google/protobuf/io/coded_stream.h | 2 | ||||
-rw-r--r-- | src/google/protobuf/util/internal/default_value_objectwriter.cc | 31 | ||||
-rw-r--r-- | src/google/protobuf/util/internal/default_value_objectwriter.h | 20 | ||||
-rw-r--r-- | src/google/protobuf/util/json_format_proto3.proto | 6 | ||||
-rw-r--r-- | src/google/protobuf/util/json_util.cc | 2 | ||||
-rw-r--r-- | src/google/protobuf/util/json_util_test.cc | 24 |
12 files changed, 400 insertions, 36 deletions
@@ -19,15 +19,14 @@ config_setting( # Protobuf Runtime Library ################################################################################ -WIN_COPTS = [ +MSVC_COPTS = [ "/DHAVE_PTHREAD", "/wd4018", # -Wno-sign-compare "/wd4514", # -Wno-unused-function ] COPTS = select({ - ":windows" : WIN_COPTS, - ":windows_msvc" : WIN_COPTS, + ":msvc" : MSVC_COPTS, "//conditions:default": [ "-DHAVE_PTHREAD", "-Wall", @@ -41,13 +40,8 @@ COPTS = select({ }) config_setting( - name = "windows", - values = { "cpu": "x64_windows" }, -) - -config_setting( - name = "windows_msvc", - values = { "cpu": "x64_windows_msvc" }, + name = "msvc", + values = { "compiler": "msvc-cl" }, ) config_setting( @@ -57,11 +51,10 @@ config_setting( }, ) -# Android and Windows builds do not need to link in a separate pthread library. +# Android and MSVC builds do not need to link in a separate pthread library. LINK_OPTS = select({ ":android": [], - ":windows": [], - ":windows_msvc": [], + ":msvc": [], "//conditions:default": ["-lpthread", "-lm"], }) diff --git a/benchmarks/README.md b/benchmarks/README.md index 74c042d8..ba906c15 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -3,7 +3,9 @@ This directory contains benchmarking schemas and data sets that you can use to test a variety of performance scenarios against your -protobuf language runtime. +protobuf language runtime. If you are looking for performance +numbers of officially support languages, see [here]( +https://github.com/google/protobuf/blob/master/docs/Performance.md) ## Prerequisite @@ -17,6 +19,11 @@ We are using [google/benchmark](https://github.com/google/benchmark) as the benchmark tool for testing cpp. This will be automaticly made during build the cpp benchmark. +The cpp protobuf performance can be improved by linking with [tcmalloc library]( +https://gperftools.github.io/gperftools/tcmalloc.html). For using tcmalloc, you +need to build [gpertools](https://github.com/gperftools/gperftools) to generate +libtcmallc.so library. + ### Java We're using maven to build the java benchmarks, which is the same as to build the Java protobuf. There're no other tools need to install. We're using @@ -79,6 +86,12 @@ $ make java $ make cpp ``` +For linking with tcmalloc: + +``` +$ env LD_PRELOAD={directory to libtcmalloc.so} make cpp +``` + ### Python: We have three versions of python protobuf implementation: pure python, cpp diff --git a/conformance/failure_list_ruby.txt b/conformance/failure_list_ruby.txt index 1bab1359..b2683372 100644 --- a/conformance/failure_list_ruby.txt +++ b/conformance/failure_list_ruby.txt @@ -120,6 +120,8 @@ Required.Proto3.JsonInput.ValueAcceptInteger.JsonOutput Required.Proto3.JsonInput.ValueAcceptInteger.ProtobufOutput Required.Proto3.JsonInput.ValueAcceptList.JsonOutput Required.Proto3.JsonInput.ValueAcceptList.ProtobufOutput +Required.Proto3.JsonInput.ValueAcceptListWithNull.JsonOutput +Required.Proto3.JsonInput.ValueAcceptListWithNull.ProtobufOutput Required.Proto3.JsonInput.ValueAcceptNull.JsonOutput Required.Proto3.JsonInput.ValueAcceptNull.ProtobufOutput Required.Proto3.JsonInput.ValueAcceptObject.JsonOutput diff --git a/docs/performance.md b/docs/performance.md new file mode 100644 index 00000000..6a5316c8 --- /dev/null +++ b/docs/performance.md @@ -0,0 +1,304 @@ +# Protobuf Perforamcne +This benchmark result is tested on workstation with processor of Intel® Xeon® Processor E5-2630 and 32GB RAM + +This table contains 3 languages' results: + +* **C++** - For C++ there're 3 kinds of parsing ways: + * **new** - This is for using new operator for creating message instance. + * **new arena** - This is for using arena for creating new message instance. + * **reuse** - This is for reusing the same message instance for parsing. +* **Java** - For Java there're 3 kinds of parsing/Serialization ways: + * **byte[]** - This is for parsing from a Byte Array. + * **ByteString** - This is for parsing from a + com.google.protobuf.ByteString. + * **InputStream** - This is for parsing from a InputStream +* **Python** - For Pythong there're 3 kinds of python protobuf for testing: + * **C++-genereated-code** - This is for using cpp generated code of the + proto file as dynamic linked library. + * **C++-reflection** - This is for using cpp reflection, which there's no + generated code, but still using cpp protobuf library as dynamic linked + library. + * **pure-Python** - This is for pure Python version, which don't link with + any cpp protobuf library. + +## Parsing performance + +<table> +<tbody><tr> +<th rowspan="2"> </th> +<th colspan="3" rowspan="1">C++</th> +<th colspan="3" rowspan="1">C++ with tcmalloc</th> +<th colspan="3" rowspan="1">java</th> +<th colspan="3" rowspan="1">python</th> +</tr> +<tr> +<th colspan="1">new</th> +<th colspan="1">new arena</th> +<th colspan="1">reuse</th> +<th colspan="1">new</th> +<th colspan="1">new arena</th> +<th colspan="1">reuse</th> +<th colspan="1">byte[]</th> +<th colspan="1">ByteString</th> +<th colspan="1">InputStream</th> +<th colspan="1">C++-generated-code</th> +<th colspan="1">C++-reflection</th> +<th colspan="1">pure-Python</th> +</tr> +<tr> +<td>google_message1_proto2</td> +<td>368.717MB/s</td> +<td>261.847MB/s</td> +<td>799.403MB/s</td> +<td>645.183MB/s</td> +<td>441.023MB/s</td> +<td>1.122GB/s</td> +<td>425.437MB/s</td> +<td>425.937MB/s</td> +<td>251.018MB/s</td> +<td>82.8314MB/s</td> +<td>47.6763MB/s</td> +<td>3.76299MB/s</td> +</tr> +<tr> +<td>google_message1_proto3</td> +<td>294.517MB/s</td> +<td>229.116MB/s</td> +<td>469.982MB/s</td> +<td>434.510MB/s</td> +<td>394.701MB/s</td> +<td>591.931MB/s</td> +<td>357.597MB/s</td> +<td>378.568MB/s</td> +<td>221.676MB/s</td> +<td>82.0498MB/s</td> +<td>39.9467MB/s</td> +<td>3.77751MB/s</td> +</tr> +<tr> +<td>google_message2</td> +<td>277.242MB/s</td> +<td>347.611MB/s</td> +<td>793.67MB/s</td> +<td>503.721MB/s</td> +<td>596.333MB/s</td> +<td>922.533MB/s</td> +<td>416.778MB/s</td> +<td>419.543MB/s</td> +<td>367.145MB/s</td> +<td>241.46MB/s</td> +<td>71.5723MB/s</td> +<td>2.73538MB/s</td> +</tr> +<tr> +<td>google_message3_1</td> +<td>213.478MB/s</td> +<td>291.58MB/s</td> +<td>543.398MB/s</td> +<td>539.704MB/s</td> +<td>717.300MB/s</td> +<td>927.333MB/s</td> +<td>684.241MB/s</td> +<td>704.47MB/s</td> +<td>648.624MB/s</td> +<td>209.036MB/s</td> +<td>142.356MB/s</td> +<td>15.3324MB/s</td> +</tr> +<tr> +<td>google_message3_2</td> +<td>672.685MB/s</td> +<td>802.767MB/s</td> +<td>1.21505GB/s</td> +<td>985.790MB/s</td> +<td>1.136GB/s</td> +<td>1.367GB/s</td> +<td>1.54439GB/s</td> +<td>1.60603GB/s</td> +<td>1.33443GB/s</td> +<td>573.835MB/s</td> +<td>314.33MB/s</td> +<td>15.0169MB/s</td> +</tr> +<tr> +<td>google_message3_3</td> +<td>207.681MB/s</td> +<td>140.591MB/s</td> +<td>535.181MB/s</td> +<td>369.743MB/s</td> +<td>262.301MB/s</td> +<td>556.644MB/s</td> +<td>279.385MB/s</td> +<td>304.853MB/s</td> +<td>107.575MB/s</td> +<td>32.248MB/s</td> +<td>26.1431MB/s</td> +<td>2.63541MB/s</td> +</tr> +<tr> +<td>google_message3_4</td> +<td>7.96091GB/s</td> +<td>7.10024GB/s</td> +<td>9.3013GB/s</td> +<td>8.518GB/s</td> +<td>8.171GB/s</td> +<td>9.917GB/s</td> +<td>5.78006GB/s</td> +<td>5.85198GB/s</td> +<td>4.62609GB/s</td> +<td>2.49631GB/s</td> +<td>2.35442GB/s</td> +<td>802.061MB/s</td> +</tr> +<tr> +<td>google_message3_5</td> +<td>76.0072MB/s</td> +<td>51.6769MB/s</td> +<td>237.856MB/s</td> +<td>178.495MB/s</td> +<td>111.751MB/s</td> +<td>329.569MB/s</td> +<td>121.038MB/s</td> +<td>132.866MB/s</td> +<td>36.9197MB/s</td> +<td>10.3962MB/s</td> +<td>8.84659MB/s</td> +<td>1.25203MB/s</td> +</tr> +<tr> +<td>google_message4</td> +<td>331.46MB/s</td> +<td>404.862MB/s</td> +<td>427.99MB/s</td> +<td>589.887MB/s</td> +<td>720.367MB/s</td> +<td>705.373MB/s</td> +<td>606.228MB/s</td> +<td>589.13MB/s</td> +<td>530.692MB/s</td> +<td>305.543MB/s</td> +<td>174.834MB/s</td> +<td>7.86485MB/s</td> +</tr> +</tbody></table> + +## Serialization performance + +<table> +<tbody><tr> +<th rowspan="2"> </th> +<th colspan="1" rowspan="2">C++</th> +<th colspan="1" rowspan="2">C++ with tcmalloc</th> +<th colspan="3" rowspan="1">java</th> +<th colspan="3" rowspan="1">python</th> +</tr> +<tr> +<th colspan="1">byte[]</th> +<th colspan="1">ByteString</th> +<th colspan="1">InputStream</th> +<th colspan="1">C++-generated-code</th> +<th colspan="1">C++-reflection</th> +<th colspan="1">pure-Python</th> +</tr> +<tr> +<td>google_message1_proto2</td> +<td>1.39698GB/s</td> +<td>1.701GB/s</td> +<td>1.12915GB/s</td> +<td>1.13589GB/s</td> +<td>758.609MB/s</td> +<td>260.911MB/s</td> +<td>58.4815MB/s</td> +<td>5.77824MB/s</td> +</tr> +<tr> +<td>google_message1_proto3</td> +<td>959.305MB/s</td> +<td>939.404MB/s</td> +<td>1.15372GB/s</td> +<td>1.07824GB/s</td> +<td>802.337MB/s</td> +<td>239.4MB/s</td> +<td>33.6336MB/s</td> +<td>5.80524MB/s</td> +</tr> +<tr> +<td>google_message2</td> +<td>1.27429GB/s</td> +<td>1.402GB/s</td> +<td>1.01039GB/s</td> +<td>1022.99MB/s</td> +<td>798.736MB/s</td> +<td>996.755MB/s</td> +<td>57.9601MB/s</td> +<td>4.09246MB/s</td> +</tr> +<tr> +<td>google_message3_1</td> +<td>1.31916GB/s</td> +<td>2.049GB/s</td> +<td>991.496MB/s</td> +<td>860.332MB/s</td> +<td>662.88MB/s</td> +<td>1.48625GB/s</td> +<td>421.287MB/s</td> +<td>18.002MB/s</td> +</tr> +<tr> +<td>google_message3_2</td> +<td>2.15676GB/s</td> +<td>2.632GB/s</td> +<td>2.14736GB/s</td> +<td>2.08136GB/s</td> +<td>1.55997GB/s</td> +<td>2.39597GB/s</td> +<td>326.777MB/s</td> +<td>16.0527MB/s</td> +</tr> +<tr> +<td>google_message3_3</td> +<td>650.456MB/s</td> +<td>1.040GB/s</td> +<td>593.52MB/s</td> +<td>580.667MB/s</td> +<td>346.839MB/s</td> +<td>123.978MB/s</td> +<td>35.893MB/s</td> +<td>2.32834MB/s</td> +</tr> +<tr> +<td>google_message3_4</td> +<td>8.70154GB/s</td> +<td>9.825GB/s</td> +<td>5.88645GB/s</td> +<td>5.93946GB/s</td> +<td>2.44388GB/s</td> +<td>5.9241GB/s</td> +<td>4.05837GB/s</td> +<td>876.87MB/s</td> +</tr> +<tr> +<td>google_message3_5</td> +<td>246.33MB/s</td> +<td>443.993MB/s</td> +<td>283.278MB/s</td> +<td>259.167MB/s</td> +<td>206.37MB/s</td> +<td>37.0285MB/s</td> +<td>12.2228MB/s</td> +<td>1.1979MB/s</td> +</tr> +<tr> +<td>google_message4</td> +<td>1.56674GB/s</td> +<td>2.19601GB/s</td> +<td>776.907MB/s</td> +<td>770.707MB/s</td> +<td>702.931MB/s</td> +<td>1.49623GB/s</td> +<td>205.116MB/s</td> +<td>8.93428MB/s</td> +</tr> +</tbody></table> + +\* The cpp performance can be improved by using [tcmalloc](https://gperftools.github.io/gperftools/tcmalloc.html), please follow the (instruction)[https://github.com/google/protobuf/blob/master/benchmarks/README.md] to link with tcmalloc to get the faster result. diff --git a/src/google/protobuf/compiler/php/php_generator.h b/src/google/protobuf/compiler/php/php_generator.h index 67e70bc7..54a95ef5 100644 --- a/src/google/protobuf/compiler/php/php_generator.h +++ b/src/google/protobuf/compiler/php/php_generator.h @@ -54,9 +54,12 @@ class LIBPROTOC_EXPORT Generator // To skip reserved keywords in php, some generated classname are prefixed. // Other code generators may need following API to figure out the actual // classname. -std::string GeneratedClassName(const google::protobuf::Descriptor* desc); -std::string GeneratedClassName(const google::protobuf::EnumDescriptor* desc); -std::string GeneratedClassName(const google::protobuf::ServiceDescriptor* desc); +LIBPROTOC_EXPORT std::string GeneratedClassName( + const google::protobuf::Descriptor* desc); +LIBPROTOC_EXPORT std::string GeneratedClassName( + const google::protobuf::EnumDescriptor* desc); +LIBPROTOC_EXPORT std::string GeneratedClassName( + const google::protobuf::ServiceDescriptor* desc); } // namespace php } // namespace compiler diff --git a/src/google/protobuf/generated_message_util.h b/src/google/protobuf/generated_message_util.h index c0e41bf7..c26e3e52 100644 --- a/src/google/protobuf/generated_message_util.h +++ b/src/google/protobuf/generated_message_util.h @@ -175,7 +175,7 @@ struct LIBPROTOBUF_EXPORT FieldMetadata { inline bool IsPresent(const void* base, uint32 hasbit) { const uint32* has_bits_array = static_cast<const uint32*>(base); - return has_bits_array[hasbit / 32] & (1u << (hasbit & 31)); + return (has_bits_array[hasbit / 32] & (1u << (hasbit & 31))) != 0; } inline bool IsOneofPresent(const void* base, uint32 offset, uint32 tag) { diff --git a/src/google/protobuf/io/coded_stream.h b/src/google/protobuf/io/coded_stream.h index 9dad1c61..0f70ecde 100644 --- a/src/google/protobuf/io/coded_stream.h +++ b/src/google/protobuf/io/coded_stream.h @@ -851,7 +851,7 @@ class LIBPROTOBUF_EXPORT CodedOutputStream { } static bool IsDefaultSerializationDeterministic() { - return default_serialization_deterministic_.load(std::memory_order_relaxed); + return default_serialization_deterministic_.load(std::memory_order_relaxed) != 0; } private: diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.cc b/src/google/protobuf/util/internal/default_value_objectwriter.cc index 2826e90e..b41feb7a 100644 --- a/src/google/protobuf/util/internal/default_value_objectwriter.cc +++ b/src/google/protobuf/util/internal/default_value_objectwriter.cc @@ -66,6 +66,7 @@ DefaultValueObjectWriter::DefaultValueObjectWriter( root_(nullptr), suppress_empty_list_(false), preserve_proto_field_names_(false), + use_ints_for_enums_(false), field_scrub_callback_(nullptr), ow_(ow) {} @@ -200,10 +201,10 @@ DefaultValueObjectWriter::Node* DefaultValueObjectWriter::CreateNewNode( DefaultValueObjectWriter::Node* DefaultValueObjectWriter::CreateNewNode( const string& name, const google::protobuf::Type* type, NodeKind kind, const DataPiece& data, bool is_placeholder, const std::vector<string>& path, - bool suppress_empty_list, bool preserve_proto_field_names, + bool suppress_empty_list, bool preserve_proto_field_names, bool use_ints_for_enums, FieldScrubCallBack* field_scrub_callback) { return new Node(name, type, kind, data, is_placeholder, path, - suppress_empty_list, preserve_proto_field_names, + suppress_empty_list, preserve_proto_field_names, use_ints_for_enums, field_scrub_callback); } @@ -220,12 +221,13 @@ DefaultValueObjectWriter::Node::Node( path_(path), suppress_empty_list_(suppress_empty_list), preserve_proto_field_names_(false), + use_ints_for_enums_(false), field_scrub_callback_(field_scrub_callback) {} DefaultValueObjectWriter::Node::Node( const string& name, const google::protobuf::Type* type, NodeKind kind, const DataPiece& data, bool is_placeholder, const std::vector<string>& path, - bool suppress_empty_list, bool preserve_proto_field_names, + bool suppress_empty_list, bool preserve_proto_field_names, bool use_ints_for_enums, FieldScrubCallBack* field_scrub_callback) : name_(name), type_(type), @@ -236,6 +238,7 @@ DefaultValueObjectWriter::Node::Node( path_(path), suppress_empty_list_(suppress_empty_list), preserve_proto_field_names_(preserve_proto_field_names), + use_ints_for_enums_(use_ints_for_enums), field_scrub_callback_(field_scrub_callback) {} DefaultValueObjectWriter::Node* DefaultValueObjectWriter::Node::FindChild( @@ -408,9 +411,9 @@ void DefaultValueObjectWriter::Node::PopulateChildren( std::unique_ptr<Node> child(new Node( preserve_proto_field_names_ ? field.name() : field.json_name(), field_type, kind, - kind == PRIMITIVE ? CreateDefaultDataPieceForField(field, typeinfo) + kind == PRIMITIVE ? CreateDefaultDataPieceForField(field, typeinfo, use_ints_for_enums_) : DataPiece::NullData(), - true, path, suppress_empty_list_, preserve_proto_field_names_, + true, path, suppress_empty_list_, preserve_proto_field_names_, use_ints_for_enums_, field_scrub_callback_)); new_children.push_back(child.release()); } @@ -435,7 +438,7 @@ void DefaultValueObjectWriter::MaybePopulateChildrenOfAny(Node* node) { } DataPiece DefaultValueObjectWriter::FindEnumDefault( - const google::protobuf::Field& field, const TypeInfo* typeinfo) { + const google::protobuf::Field& field, const TypeInfo* typeinfo, bool use_ints_for_enums) { if (!field.default_value().empty()) return DataPiece(field.default_value(), true); @@ -448,12 +451,12 @@ DataPiece DefaultValueObjectWriter::FindEnumDefault( } // We treat the first value as the default if none is specified. return enum_type->enumvalue_size() > 0 - ? DataPiece(enum_type->enumvalue(0).name(), true) + ? (use_ints_for_enums ? DataPiece(enum_type->enumvalue(0).number()) : DataPiece(enum_type->enumvalue(0).name(), true)) : DataPiece::NullData(); } DataPiece DefaultValueObjectWriter::CreateDefaultDataPieceForField( - const google::protobuf::Field& field, const TypeInfo* typeinfo) { + const google::protobuf::Field& field, const TypeInfo* typeinfo, bool use_ints_for_enums) { switch (field.kind()) { case google::protobuf::Field_Kind_TYPE_DOUBLE: { return DataPiece(ConvertTo<double>( @@ -496,7 +499,7 @@ DataPiece DefaultValueObjectWriter::CreateDefaultDataPieceForField( field.default_value(), &DataPiece::ToUint32, static_cast<uint32>(0))); } case google::protobuf::Field_Kind_TYPE_ENUM: { - return FindEnumDefault(field, typeinfo); + return FindEnumDefault(field, typeinfo, use_ints_for_enums); } default: { return DataPiece::NullData(); } } @@ -508,7 +511,7 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject( std::vector<string> path; root_.reset(CreateNewNode(string(name), &type_, OBJECT, DataPiece::NullData(), false, path, - suppress_empty_list_, preserve_proto_field_names_, + suppress_empty_list_, preserve_proto_field_names_, use_ints_for_enums_, field_scrub_callback_.get())); root_->PopulateChildren(typeinfo_); current_ = root_.get(); @@ -526,7 +529,7 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject( : nullptr), OBJECT, DataPiece::NullData(), false, child == nullptr ? current_->path() : child->path(), - suppress_empty_list_, preserve_proto_field_names_, + suppress_empty_list_, preserve_proto_field_names_, use_ints_for_enums_, field_scrub_callback_.get())); child = node.get(); current_->AddChild(node.release()); @@ -559,7 +562,7 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::StartList( std::vector<string> path; root_.reset(CreateNewNode(string(name), &type_, LIST, DataPiece::NullData(), false, path, suppress_empty_list_, - preserve_proto_field_names_, + preserve_proto_field_names_, use_ints_for_enums_, field_scrub_callback_.get())); current_ = root_.get(); return this; @@ -570,7 +573,7 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::StartList( std::unique_ptr<Node> node( CreateNewNode(string(name), nullptr, LIST, DataPiece::NullData(), false, child == nullptr ? current_->path() : child->path(), - suppress_empty_list_, preserve_proto_field_names_, + suppress_empty_list_, preserve_proto_field_names_, use_ints_for_enums_, field_scrub_callback_.get())); child = node.get(); current_->AddChild(node.release()); @@ -632,7 +635,7 @@ void DefaultValueObjectWriter::RenderDataPiece(StringPiece name, std::unique_ptr<Node> node( CreateNewNode(string(name), nullptr, PRIMITIVE, data, false, child == nullptr ? current_->path() : child->path(), - suppress_empty_list_, preserve_proto_field_names_, + suppress_empty_list_, preserve_proto_field_names_, use_ints_for_enums_, field_scrub_callback_.get())); current_->AddChild(node.release()); } else { diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.h b/src/google/protobuf/util/internal/default_value_objectwriter.h index 02cf827a..6e71f9c8 100644 --- a/src/google/protobuf/util/internal/default_value_objectwriter.h +++ b/src/google/protobuf/util/internal/default_value_objectwriter.h @@ -128,6 +128,12 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { preserve_proto_field_names_ = value; } + // If set to true, enums are rendered as ints from output when default values + // are written. + void set_print_enums_as_ints(bool value) { + use_ints_for_enums_ = value; + } + protected: enum NodeKind { PRIMITIVE = 0, @@ -147,7 +153,7 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { Node(const string& name, const google::protobuf::Type* type, NodeKind kind, const DataPiece& data, bool is_placeholder, const std::vector<string>& path, bool suppress_empty_list, - bool preserve_proto_field_names, + bool preserve_proto_field_names, bool use_ints_for_enums, FieldScrubCallBack* field_scrub_callback); virtual ~Node() { for (int i = 0; i < children_.size(); ++i) { @@ -230,6 +236,9 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { // Whether to preserve original proto field names bool preserve_proto_field_names_; + // Whether to always print enums as ints + bool use_ints_for_enums_; + // Pointer to function for determining whether a field needs to be scrubbed // or not. This callback is owned by the creator of this node. FieldScrubCallBack* field_scrub_callback_; @@ -253,11 +262,12 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { const std::vector<string>& path, bool suppress_empty_list, bool preserve_proto_field_names, + bool use_ints_for_enums, FieldScrubCallBack* field_scrub_callback); // Creates a DataPiece containing the default value of the type of the field. static DataPiece CreateDefaultDataPieceForField( - const google::protobuf::Field& field, const TypeInfo* typeinfo); + const google::protobuf::Field& field, const TypeInfo* typeinfo, bool use_ints_for_enums); protected: // Returns a pointer to current Node in tree. @@ -279,7 +289,8 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { // 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); + const TypeInfo* typeinfo, + bool use_ints_for_enums); // Type information for all the types used in the descriptor. Used to find // google::protobuf::Type of nested messages/enums. @@ -304,6 +315,9 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { // Whether to preserve original proto field names bool preserve_proto_field_names_; + // Whether to always print enums as ints + bool use_ints_for_enums_; + // Unique Pointer to function for determining whether a field needs to be // scrubbed or not. FieldScrubCallBackPtr field_scrub_callback_; diff --git a/src/google/protobuf/util/json_format_proto3.proto b/src/google/protobuf/util/json_format_proto3.proto index 8a0441c8..cbc3f81f 100644 --- a/src/google/protobuf/util/json_format_proto3.proto +++ b/src/google/protobuf/util/json_format_proto3.proto @@ -181,3 +181,9 @@ message TestCustomJsonName { message TestExtensions { .protobuf_unittest.TestAllExtensions extensions = 1; } + +message TestEnumValue{ + EnumType enum_value1 = 1; + EnumType enum_value2 = 2; + EnumType enum_value3 = 3; +}
\ No newline at end of file diff --git a/src/google/protobuf/util/json_util.cc b/src/google/protobuf/util/json_util.cc index ea0cc2e4..f81a7a30 100644 --- a/src/google/protobuf/util/json_util.cc +++ b/src/google/protobuf/util/json_util.cc @@ -97,6 +97,8 @@ util::Status BinaryToJsonStream(TypeResolver* resolver, resolver, type, &json_writer); default_value_writer.set_preserve_proto_field_names( options.preserve_proto_field_names); + default_value_writer.set_print_enums_as_ints( + options.always_print_enums_as_ints); return proto_source.WriteTo(&default_value_writer); } else { return proto_source.WriteTo(&json_writer); diff --git a/src/google/protobuf/util/json_util_test.cc b/src/google/protobuf/util/json_util_test.cc index dbd262f6..ed9092df 100644 --- a/src/google/protobuf/util/json_util_test.cc +++ b/src/google/protobuf/util/json_util_test.cc @@ -54,6 +54,7 @@ using proto3::BAR; using proto3::TestMessage; using proto3::TestMap; using proto3::TestOneof; +using proto3::TestEnumValue; static const char kTypeUrlPrefix[] = "type.googleapis.com"; @@ -217,6 +218,29 @@ TEST_F(JsonUtilTest, TestAlwaysPrintEnumsAsInts) { EXPECT_EQ(proto3::BAR, parsed.repeated_enum_value(1)); } +TEST_F(JsonUtilTest, TestPrintEnumsAsIntsWithDefaultValue) { + TestEnumValue orig; + //orig.set_enum_value1(proto3::FOO) + orig.set_enum_value2(proto3::FOO); + orig.set_enum_value3(proto3::BAR); + + JsonPrintOptions print_options; + print_options.always_print_enums_as_ints = true; + print_options.always_print_primitive_fields = true; + + string expected_json = "{\"enumValue1\":0,\"enumValue2\":0,\"enumValue3\":1}"; + EXPECT_EQ(expected_json, ToJson(orig, print_options)); + + TestEnumValue parsed; + JsonParseOptions parse_options; + ASSERT_TRUE(FromJson(expected_json, &parsed, parse_options)); + + EXPECT_EQ(proto3::FOO, parsed.enum_value1()); + EXPECT_EQ(proto3::FOO, parsed.enum_value2()); + EXPECT_EQ(proto3::BAR, parsed.enum_value3()); + +} + TEST_F(JsonUtilTest, ParseMessage) { // Some random message but good enough to verify that the parsing warpper // functions are working properly. |