aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlas Kirazci <ulas@google.com>2013-04-01 11:29:43 -0700
committerUlas Kirazci <ulas@google.com>2013-07-29 09:35:44 -0700
commit35d9fd84ff1d9ecdb91156b757cc9fdcc3f25249 (patch)
treebf47056d6c723ec8939717efb9a5f30cbe7783af
parent021f8f1badf1c4db519e3f35d600dec4b5c52eff (diff)
downloadexternal_protobuf-35d9fd84ff1d9ecdb91156b757cc9fdcc3f25249.zip
external_protobuf-35d9fd84ff1d9ecdb91156b757cc9fdcc3f25249.tar.gz
external_protobuf-35d9fd84ff1d9ecdb91156b757cc9fdcc3f25249.tar.bz2
Add an option to inspect "has" state upon parse.
If has is set, also always serialize. Change-Id: I2c8450f7ab9e837d722123dd1042991c0258ede3
-rw-r--r--java/README.txt16
-rw-r--r--java/pom.xml8
-rw-r--r--java/src/test/java/com/google/protobuf/NanoTest.java88
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_enum_field.cc26
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_generator.cc2
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_message.cc9
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_params.h11
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_primitive_field.cc64
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_primitive_field.h2
-rw-r--r--src/google/protobuf/unittest_has_nano.proto78
10 files changed, 273 insertions, 31 deletions
diff --git a/java/README.txt b/java/README.txt
index dac2e3c..58ccb88 100644
--- a/java/README.txt
+++ b/java/README.txt
@@ -301,6 +301,22 @@ message's constructor or clear() function is called, the default value
penalty. This is not a problem if the field has no default or is an
empty default.
+Nano Generator options
+
+java_nano_generate_has:
+ If true, generates a public boolean variable has<fieldname>
+ accompanying the optional or required field (not present for
+ repeated fields, groups or messages). It is set to false initially
+ and upon clear(). If parseFrom(...) reads the field from the wire,
+ it is set to true. This is a way for clients to inspect the "has"
+ value upon parse. If it is set to true, writeTo(...) will ALWAYS
+ output that field (even if field value is equal to its
+ default).
+
+ IMPORTANT: This option costs an extra 4 bytes per primitive field in
+ the message. Think carefully about whether you really need this. In
+ many cases reading the default works and determining whether the
+ field was received over the wire is irrelevant.
To use nano protobufs:
diff --git a/java/pom.xml b/java/pom.xml
index 399540f..7970f72 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -117,6 +117,7 @@
<arg value="../src/google/protobuf/unittest_enormous_descriptor.proto" />
<arg value="../src/google/protobuf/unittest_no_generic_services.proto" />
</exec>
+ <!-- java micro -->
<exec executable="../src/protoc">
<arg value="--javamicro_out=opt=speed,java_use_vector=false,java_package=google/protobuf/unittest_import_micro.proto|com.google.protobuf.micro,java_outer_classname=google/protobuf/unittest_import_micro.proto|UnittestImportMicro:target/generated-test-sources" />
<arg value="--proto_path=../src" />
@@ -128,6 +129,7 @@
<arg value="../src/google/protobuf/unittest_import_micro.proto" />
<arg value="../src/google/protobuf/unittest_multiple_micro.proto" />
</exec>
+ <!-- java nano -->
<exec executable="../src/protoc">
<arg value="--javanano_out=java_package=google/protobuf/unittest_import_nano.proto|com.google.protobuf.nano,java_outer_classname=google/protobuf/unittest_import_nano.proto|UnittestImportNano:target/generated-test-sources" />
<arg value="--proto_path=../src" />
@@ -146,6 +148,12 @@
<arg value="--proto_path=src/test/java" />
<arg value="../src/google/protobuf/unittest_extension_nano.proto" />
</exec>
+ <exec executable="../src/protoc">
+ <arg value="--javanano_out=java_nano_generate_has=true:target/generated-test-sources" />
+ <arg value="--proto_path=../src" />
+ <arg value="--proto_path=src/test/java" />
+ <arg value="../src/google/protobuf/unittest_has_nano.proto" />
+ </exec>
</tasks>
<testSourceRoot>target/generated-test-sources</testSourceRoot>
<!--testSourceRoot>target/generated-test-sources/opt-space</testSourceRoot-->
diff --git a/java/src/test/java/com/google/protobuf/NanoTest.java b/java/src/test/java/com/google/protobuf/NanoTest.java
index 0ea80d4..80f091b 100644
--- a/java/src/test/java/com/google/protobuf/NanoTest.java
+++ b/java/src/test/java/com/google/protobuf/NanoTest.java
@@ -37,6 +37,7 @@ import com.google.protobuf.nano.InternalNano;
import com.google.protobuf.nano.MessageNano;
import com.google.protobuf.nano.MultipleImportingNonMultipleNano1;
import com.google.protobuf.nano.MultipleImportingNonMultipleNano2;
+import com.google.protobuf.nano.NanoHasOuterClass.TestAllTypesNanoHas;
import com.google.protobuf.nano.NanoOuterClass;
import com.google.protobuf.nano.NanoOuterClass.TestAllTypesNano;
import com.google.protobuf.nano.RecursiveMessageNano;
@@ -2095,6 +2096,93 @@ public class NanoTest extends TestCase {
}
}
+ public void testNanoWithHasParseFrom() throws Exception {
+ TestAllTypesNanoHas msg = null;
+ // Test false on creation, after clear and upon empty parse.
+ for (int i = 0; i < 3; i++) {
+ if (i == 0) {
+ msg = new TestAllTypesNanoHas();
+ } else if (i == 1) {
+ msg.clear();
+ } else if (i == 2) {
+ msg = TestAllTypesNanoHas.parseFrom(new byte[0]);
+ }
+ assertFalse(msg.hasOptionalInt32);
+ assertFalse(msg.hasOptionalString);
+ assertFalse(msg.hasOptionalBytes);
+ assertFalse(msg.hasOptionalNestedEnum);
+ assertFalse(msg.hasDefaultInt32);
+ assertFalse(msg.hasDefaultString);
+ assertFalse(msg.hasDefaultBytes);
+ assertFalse(msg.hasDefaultFloatNan);
+ assertFalse(msg.hasDefaultNestedEnum);
+ assertFalse(msg.hasId);
+ msg.optionalInt32 = 123;
+ msg.optionalNestedMessage = new TestAllTypesNanoHas.NestedMessage();
+ msg.optionalNestedMessage.bb = 2;
+ msg.optionalNestedEnum = TestAllTypesNano.BAZ;
+ }
+
+ byte [] result = MessageNano.toByteArray(msg);
+ int msgSerializedSize = msg.getSerializedSize();
+ //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length);
+ assertTrue(msgSerializedSize == 13);
+ assertEquals(result.length, msgSerializedSize);
+
+ // Has fields true upon parse.
+ TestAllTypesNanoHas newMsg = TestAllTypesNanoHas.parseFrom(result);
+ assertEquals(123, newMsg.optionalInt32);
+ assertTrue(newMsg.hasOptionalInt32);
+ assertEquals(2, newMsg.optionalNestedMessage.bb);
+ assertTrue(newMsg.optionalNestedMessage.hasBb);
+ assertEquals(TestAllTypesNanoHas.BAZ, newMsg.optionalNestedEnum);
+ assertTrue(newMsg.hasOptionalNestedEnum);
+ }
+
+ public void testNanoWithHasSerialize() throws Exception {
+ TestAllTypesNanoHas msg = new TestAllTypesNanoHas();
+ msg.hasOptionalInt32 = true;
+ msg.hasOptionalString = true;
+ msg.hasOptionalBytes = true;
+ msg.optionalNestedMessage = new TestAllTypesNanoHas.NestedMessage();
+ msg.optionalNestedMessage.hasBb = true;
+ msg.hasOptionalNestedEnum = true;
+ msg.hasDefaultInt32 = true;
+ msg.hasDefaultString = true;
+ msg.hasDefaultBytes = true;
+ msg.hasDefaultFloatNan = true;
+ msg.hasDefaultNestedEnum = true;
+
+ byte [] result = MessageNano.toByteArray(msg);
+ int msgSerializedSize = msg.getSerializedSize();
+ assertEquals(result.length, msgSerializedSize);
+
+ // Now deserialize and find that all fields are set and equal to their defaults.
+ TestAllTypesNanoHas newMsg = TestAllTypesNanoHas.parseFrom(result);
+ assertTrue(newMsg.hasOptionalInt32);
+ assertTrue(newMsg.hasOptionalString);
+ assertTrue(newMsg.hasOptionalBytes);
+ assertTrue(newMsg.optionalNestedMessage.hasBb);
+ assertTrue(newMsg.hasOptionalNestedEnum);
+ assertTrue(newMsg.hasDefaultInt32);
+ assertTrue(newMsg.hasDefaultString);
+ assertTrue(newMsg.hasDefaultBytes);
+ assertTrue(newMsg.hasDefaultFloatNan);
+ assertTrue(newMsg.hasDefaultNestedEnum);
+ assertTrue(newMsg.hasId);
+ assertEquals(0, newMsg.optionalInt32);
+ assertEquals(0, newMsg.optionalString.length());
+ assertEquals(0, newMsg.optionalBytes.length);
+ assertEquals(0, newMsg.optionalNestedMessage.bb);
+ assertEquals(TestAllTypesNanoHas.FOO, newMsg.optionalNestedEnum);
+ assertEquals(41, newMsg.defaultInt32);
+ assertEquals("hello", newMsg.defaultString);
+ assertEquals("world", new String(newMsg.defaultBytes, "UTF-8"));
+ assertEquals(TestAllTypesNanoHas.BAR, newMsg.defaultNestedEnum);
+ assertEquals(Float.NaN, newMsg.defaultFloatNan);
+ assertEquals(0, newMsg.id);
+ }
+
/**
* Tests that fields with a default value of NaN are not serialized when
* set to NaN. This is a special case as NaN != NaN, so normal equality
diff --git a/src/google/protobuf/compiler/javanano/javanano_enum_field.cc b/src/google/protobuf/compiler/javanano/javanano_enum_field.cc
index 215f341..9c4acb9 100644
--- a/src/google/protobuf/compiler/javanano/javanano_enum_field.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_enum_field.cc
@@ -82,12 +82,22 @@ void EnumFieldGenerator::
GenerateMembers(io::Printer* printer) const {
printer->Print(variables_,
"public int $name$ = $default$;\n");
+
+ if (params_.generate_has()) {
+ printer->Print(variables_,
+ "public boolean has$capitalized_name$ = false;\n");
+ }
}
void EnumFieldGenerator::
GenerateParsingCode(io::Printer* printer) const {
printer->Print(variables_,
" this.$name$ = input.readInt32();\n");
+
+ if (params_.generate_has()) {
+ printer->Print(variables_,
+ " has$capitalized_name$ = true;\n");
+ }
}
void EnumFieldGenerator::
@@ -96,8 +106,14 @@ GenerateSerializationCode(io::Printer* printer) const {
printer->Print(variables_,
"output.writeInt32($number$, this.$name$);\n");
} else {
+ if (params_.generate_has()) {
+ printer->Print(variables_,
+ "if (this.$name$ != $default$ || has$capitalized_name$) {\n");
+ } else {
+ printer->Print(variables_,
+ "if (this.$name$ != $default$) {\n");
+ }
printer->Print(variables_,
- "if (this.$name$ != $default$) {\n"
" output.writeInt32($number$, this.$name$);\n"
"}\n");
}
@@ -110,8 +126,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
"size += com.google.protobuf.nano.CodedOutputByteBufferNano\n"
" .computeInt32Size($number$, this.$name$);\n");
} else {
+ if (params_.generate_has()) {
+ printer->Print(variables_,
+ "if (this.$name$ != $default$ || has$capitalized_name$) {\n");
+ } else {
+ printer->Print(variables_,
+ "if (this.$name$ != $default$) {\n");
+ }
printer->Print(variables_,
- "if (this.$name$ != $default$) {\n"
" size += com.google.protobuf.nano.CodedOutputByteBufferNano\n"
" .computeInt32Size($number$, this.$name$);\n"
"}\n");
diff --git a/src/google/protobuf/compiler/javanano/javanano_generator.cc b/src/google/protobuf/compiler/javanano/javanano_generator.cc
index 4b6b6a5..76e7263 100644
--- a/src/google/protobuf/compiler/javanano/javanano_generator.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_generator.cc
@@ -118,6 +118,8 @@ bool JavaNanoGenerator::Generate(const FileDescriptor* file,
params.set_store_unknown_fields(options[i].second == "true");
} else if (options[i].first == "java_multiple_files") {
params.set_override_java_multiple_files(options[i].second == "true");
+ } else if (options[i].first == "java_nano_generate_has") {
+ params.set_generate_has(options[i].second == "true");
} else {
*error = "Ignore unknown javanano generator option: " + options[i].first;
}
diff --git a/src/google/protobuf/compiler/javanano/javanano_message.cc b/src/google/protobuf/compiler/javanano/javanano_message.cc
index b5983a2..de56944 100644
--- a/src/google/protobuf/compiler/javanano/javanano_message.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_message.cc
@@ -406,6 +406,15 @@ void MessageGenerator::GenerateClear(io::Printer* printer) {
"name", RenameJavaKeywords(UnderscoresToCamelCase(field)),
"default", DefaultValue(params_, field));
}
+
+ if (params_.generate_has() &&
+ field->label() != FieldDescriptor::LABEL_REPEATED &&
+ field->type() != FieldDescriptor::TYPE_GROUP &&
+ field->type() != FieldDescriptor::TYPE_MESSAGE) {
+ printer->Print(
+ "has$capitalized_name$ = false;\n",
+ "capitalized_name", UnderscoresToCapitalizedCamelCase(field));
+ }
}
// Clear unknown fields.
diff --git a/src/google/protobuf/compiler/javanano/javanano_params.h b/src/google/protobuf/compiler/javanano/javanano_params.h
index 5c0e6f0..51686da 100644
--- a/src/google/protobuf/compiler/javanano/javanano_params.h
+++ b/src/google/protobuf/compiler/javanano/javanano_params.h
@@ -57,13 +57,15 @@ class Params {
NameMap java_packages_;
NameMap java_outer_classnames_;
NameSet java_multiple_files_;
+ bool generate_has_;
public:
Params(const string & base_name) :
empty_(""),
base_name_(base_name),
override_java_multiple_files_(JAVANANO_MUL_UNSET),
- store_unknown_fields_(false) {
+ store_unknown_fields_(false),
+ generate_has_(false) {
}
const string& base_name() const {
@@ -151,6 +153,13 @@ class Params {
return store_unknown_fields_;
}
+ void set_generate_has(bool value) {
+ generate_has_ = value;
+ }
+ bool generate_has() const {
+ return generate_has_;
+ }
+
};
} // namespace javanano
diff --git a/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc b/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc
index 987a103..20ea6b0 100644
--- a/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc
@@ -321,12 +321,46 @@ GenerateMembers(io::Printer* printer) const {
printer->Print(variables_,
"public $type$ $name$ = $default$;\n");
}
+
+ if (params_.generate_has()) {
+ printer->Print(variables_,
+ "public boolean has$capitalized_name$ = false;\n");
+ }
}
void PrimitiveFieldGenerator::
GenerateParsingCode(io::Printer* printer) const {
printer->Print(variables_,
"this.$name$ = input.read$capitalized_type$();\n");
+
+ if (params_.generate_has()) {
+ printer->Print(variables_,
+ "has$capitalized_name$ = true;\n");
+ }
+}
+
+void PrimitiveFieldGenerator::
+GenerateSerializationConditional(io::Printer* printer) const {
+ if (params_.generate_has()) {
+ printer->Print(variables_,
+ "if (has$capitalized_name$ || ");
+ } else {
+ printer->Print(variables_,
+ "if (");
+ }
+ if (IsArrayType(GetJavaType(descriptor_))) {
+ printer->Print(variables_,
+ "!java.util.Arrays.equals(this.$name$, $default$)) {\n");
+ } else if (IsReferenceType(GetJavaType(descriptor_))) {
+ printer->Print(variables_,
+ "!this.$name$.equals($default$)) {\n");
+ } else if (IsDefaultNaN(descriptor_)) {
+ printer->Print(variables_,
+ "!$capitalized_type$.isNaN(this.$name$)) {\n");
+ } else {
+ printer->Print(variables_,
+ "this.$name$ != $default$) {\n");
+ }
}
void PrimitiveFieldGenerator::
@@ -335,20 +369,7 @@ GenerateSerializationCode(io::Printer* printer) const {
printer->Print(variables_,
"output.write$capitalized_type$($number$, this.$name$);\n");
} else {
- if (IsArrayType(GetJavaType(descriptor_))) {
- printer->Print(variables_,
- "if (!java.util.Arrays.equals(this.$name$, $default$)) {\n");
- } else if (IsReferenceType(GetJavaType(descriptor_))) {
- printer->Print(variables_,
- "if (!this.$name$.equals($default$)) {\n");
- } else if (IsDefaultNaN(descriptor_)) {
- printer->Print(variables_,
- "if (!$capitalized_type$.isNaN(this.$name$)) {\n");
- } else {
- printer->Print(variables_,
- "if (this.$name$ != $default$) {\n");
- }
-
+ GenerateSerializationConditional(printer);
printer->Print(variables_,
" output.write$capitalized_type$($number$, this.$name$);\n"
"}\n");
@@ -362,20 +383,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
"size += com.google.protobuf.nano.CodedOutputByteBufferNano\n"
" .compute$capitalized_type$Size($number$, this.$name$);\n");
} else {
- if (IsArrayType(GetJavaType(descriptor_))) {
- printer->Print(variables_,
- "if (!java.util.Arrays.equals(this.$name$, $default$)) {\n");
- } else if (IsReferenceType(GetJavaType(descriptor_))) {
- printer->Print(variables_,
- "if (!this.$name$.equals($default$)) {\n");
- } else if (IsDefaultNaN(descriptor_)) {
- printer->Print(variables_,
- "if (!$capitalized_type$.isNaN(this.$name$)) {\n");
- } else {
- printer->Print(variables_,
- "if (this.$name$ != $default$) {\n");
- }
-
+ GenerateSerializationConditional(printer);
printer->Print(variables_,
" size += com.google.protobuf.nano.CodedOutputByteBufferNano\n"
" .compute$capitalized_type$Size($number$, this.$name$);\n"
diff --git a/src/google/protobuf/compiler/javanano/javanano_primitive_field.h b/src/google/protobuf/compiler/javanano/javanano_primitive_field.h
index 9b1bb46..aa6355b 100644
--- a/src/google/protobuf/compiler/javanano/javanano_primitive_field.h
+++ b/src/google/protobuf/compiler/javanano/javanano_primitive_field.h
@@ -58,6 +58,8 @@ class PrimitiveFieldGenerator : public FieldGenerator {
string GetBoxedType() const;
private:
+ void GenerateSerializationConditional(io::Printer* printer) const;
+
const FieldDescriptor* descriptor_;
map<string, string> variables_;
diff --git a/src/google/protobuf/unittest_has_nano.proto b/src/google/protobuf/unittest_has_nano.proto
new file mode 100644
index 0000000..3a1e5b5
--- /dev/null
+++ b/src/google/protobuf/unittest_has_nano.proto
@@ -0,0 +1,78 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// 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.
+
+// Author: ulas@google.com (Ulas Kirazci)
+
+package protobuf_unittest;
+
+option java_package = "com.google.protobuf.nano";
+option java_outer_classname = "NanoHasOuterClass";
+
+message TestAllTypesNanoHas {
+
+ message NestedMessage {
+ optional int32 bb = 1;
+ }
+
+ enum NestedEnum {
+ FOO = 1;
+ BAR = 2;
+ BAZ = 3;
+ }
+
+ // Singular
+ optional int32 optional_int32 = 1;
+ optional string optional_string = 14;
+ optional bytes optional_bytes = 15;
+
+ optional NestedMessage optional_nested_message = 18;
+
+ optional NestedEnum optional_nested_enum = 21;
+
+ // Repeated
+ repeated int32 repeated_int32 = 31;
+ repeated string repeated_string = 44;
+ repeated bytes repeated_bytes = 45;
+
+ repeated NestedMessage repeated_nested_message = 48;
+
+ repeated NestedEnum repeated_nested_enum = 51;
+
+ // Singular with defaults
+ optional int32 default_int32 = 61 [default = 41 ];
+ optional string default_string = 74 [default = "hello"];
+ optional bytes default_bytes = 75 [default = "world"];
+
+ optional float default_float_nan = 99 [default = nan];
+
+ optional NestedEnum default_nested_enum = 81 [default = BAR];
+
+ required int32 id = 86;
+}