aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk4
-rw-r--r--java/README.txt51
-rw-r--r--java/pom.xml16
-rw-r--r--java/src/test/java/com/google/protobuf/NanoTest.java280
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_enum_field.cc152
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_enum_field.h4
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_generator.cc59
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_helpers.cc2
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_primitive_field.cc110
-rw-r--r--src/google/protobuf/unittest_accessors_nano.proto2
-rw-r--r--src/google/protobuf/unittest_enum_validity_nano.proto28
-rw-r--r--src/google/protobuf/unittest_has_nano.proto4
12 files changed, 564 insertions, 148 deletions
diff --git a/Android.mk b/Android.mk
index c977d3c..ec28bec 100644
--- a/Android.mk
+++ b/Android.mk
@@ -374,6 +374,8 @@ LOCAL_SRC_FILES := \
LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/src
-LOCAL_PROTO_JAVA_OUTPUT_PARAMS := java_package=$(LOCAL_PATH)/src/google/protobuf/unittest_import_nano.proto|com.google.protobuf.nano,java_outer_classname=$(LOCAL_PATH)/src/google/protobuf/unittest_import_nano.proto|UnittestImportNano
+LOCAL_PROTO_JAVA_OUTPUT_PARAMS := \
+ java_package = $(LOCAL_PATH)/src/google/protobuf/unittest_import_nano.proto|com.google.protobuf.nano, \
+ java_outer_classname = $(LOCAL_PATH)/src/google/protobuf/unittest_import_nano.proto|UnittestImportNano
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/java/README.txt b/java/README.txt
index ced78d2..13865f6 100644
--- a/java/README.txt
+++ b/java/README.txt
@@ -409,33 +409,33 @@ still generated as integer constants in the message class.
Nano version
============================
-Nano is even smaller than micro, especially in the number of generated
-functions. It is like micro:
-
-- No support for descriptors and reflection;
-- Enum constants are integers with no protection against invalid
- values set to enum fields.
-
-Except:
-
-- Setter/getter/hazzer/clearer functions are opt-in.
+Nano is a special code generator and runtime library designed specially
+for Android, and is very resource-friendly in both the amount of code
+and the runtime overhead. An overview of Nano features:
+
+- No descriptors or message builders.
+- All messages are mutable; fields are public Java fields.
+- For optional fields only, encapsulation behind setter/getter/hazzer/
+ clearer functions is opt-in, which provide proper 'has' state support.
- If not opted in, has state is not available. Serialization outputs
- all fields not equal to their default. (See important implications
- below.)
+ all fields not equal to their defaults (see important implications
+ below).
+- Required fields are always serialized.
+- Enum constants are integers; protection against invalid values only
+ when parsing from the wire.
- Enum constants can be generated into container interfaces bearing
the enum's name (so the referencing code is in Java style).
-- CodedInputStreamMicro is renamed to CodedInputByteBufferNano and can
- only take byte[] (not InputStream).
-- Similar rename from CodedOutputStreamMicro to
- CodedOutputByteBufferNano.
-- Repeated fields are in arrays, not ArrayList or Vector.
+- CodedInputByteBufferNano can only take byte[] (not InputStream).
+- Similarly CodedOutputByteBufferNano can only write to byte[].
+- Repeated fields are in arrays, not ArrayList or Vector. Null array
+ elements are allowed and silently ignored.
- Full support of serializing/deserializing repeated packed fields.
+- Support of extensions.
- Unset messages/groups are null, not an immutable empty default
instance.
-- Required fields are always serialized.
- toByteArray(...) and mergeFrom(...) are now static functions of
MessageNano.
-- "bytes" are of java type byte[].
+- The 'bytes' type translates to the Java type byte[].
IMPORTANT: If you have fields with defaults and opt out of accessors
@@ -580,9 +580,9 @@ To use nano protobufs within the Android repo:
LOCAL_STATIC_JAVA_LIBRARIES variable, so you don't need to.
- Set 'LOCAL_PROTO_JAVA_OUTPUT_PARAMS := ...' in your local .mk file
for any command-line options you need. Use commas to join multiple
- options. Write all options on the same line; avoid backslash-newline
- or '+=', because they will introduce spaces in the middle of your
- options and the generator is not prepared to handle them.
+ options. In the nano flavor only, whitespace surrounding the option
+ names and values are ignored, so you can use backslash-newline or
+ '+=' to structure your make files nicely.
- The options will be applied to *all* proto files in LOCAL_SRC_FILES
when you build a Java library or package. In case different options
are needed for different proto files, build separate Java libraries
@@ -603,10 +603,9 @@ To use nano protobufs outside of Android repo:
- Invoke with --javanano_out, e.g.:
./protoc '--javanano_out=\
-java_package=src/proto/simple-data.proto|my_package,\
-java_outer_classname=src/proto/simple-data.proto|OuterName:\
-.' src/proto/simple-data.proto
-
+ java_package=src/proto/simple-data.proto|my_package,\
+ java_outer_classname=src/proto/simple-data.proto|OuterName\
+ :.' src/proto/simple-data.proto
Contributing to nano:
diff --git a/java/pom.xml b/java/pom.xml
index b74eaf4..5b49a72 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -133,7 +133,11 @@
</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,generate_equals=true:target/generated-test-sources" />
+ <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,
+ generate_equals = true
+ :target/generated-test-sources" />
<arg value="--proto_path=../src" />
<arg value="--proto_path=src/test/java" />
<arg value="../src/google/protobuf/unittest_nano.proto" />
@@ -190,6 +194,16 @@
<arg value="../src/google/protobuf/unittest_enum_class_nano.proto" />
<arg value="../src/google/protobuf/unittest_enum_class_multiple_nano.proto" />
<arg value="../src/google/protobuf/unittest_repeated_packables_nano.proto" />
+ <arg value="../src/google/protobuf/unittest_enum_validity_nano.proto" />
+ </exec>
+ <exec executable="../src/protoc">
+ <arg value="--javanano_out=
+ optional_field_style=accessors,
+ java_outer_classname=google/protobuf/unittest_enum_validity_nano.proto|EnumValidityAccessors
+ :target/generated-test-sources" />
+ <arg value="--proto_path=../src" />
+ <arg value="--proto_path=src/test/java" />
+ <arg value="../src/google/protobuf/unittest_enum_validity_nano.proto" />
</exec>
<exec executable="../src/protoc">
<arg value="--javanano_out=optional_field_style=reftypes,generate_equals=true:target/generated-test-sources" />
diff --git a/java/src/test/java/com/google/protobuf/NanoTest.java b/java/src/test/java/com/google/protobuf/NanoTest.java
index 6a761f4..9987cac 100644
--- a/java/src/test/java/com/google/protobuf/NanoTest.java
+++ b/java/src/test/java/com/google/protobuf/NanoTest.java
@@ -33,6 +33,8 @@ package com.google.protobuf;
import com.google.protobuf.nano.CodedInputByteBufferNano;
import com.google.protobuf.nano.EnumClassNanoMultiple;
import com.google.protobuf.nano.EnumClassNanos;
+import com.google.protobuf.nano.EnumValidity;
+import com.google.protobuf.nano.EnumValidityAccessors;
import com.google.protobuf.nano.Extensions;
import com.google.protobuf.nano.Extensions.AnotherMessage;
import com.google.protobuf.nano.Extensions.MessageWithGroup;
@@ -2094,6 +2096,126 @@ public class NanoTest extends TestCase {
}
/**
+ * Tests that invalid enum values from the wire are not accepted.
+ */
+ public void testNanoEnumValidity() throws Exception {
+ final int invalid = 120;
+ final int alsoInvalid = 121;
+
+ EnumValidity.M m = new EnumValidity.M();
+ // Sanity check & baseline of the assertions for the first case below.
+ assertEquals(EnumValidity.E.default_, m.optionalE);
+ assertEquals(EnumValidity.E.BAZ, m.defaultE);
+
+ m.optionalE = invalid;
+ m.defaultE = invalid;
+ // E contains all valid values
+ m.repeatedE = new int[] {EnumValidity.E.FOO, EnumValidity.E.BAR};
+ m.packedE = new int[] {EnumValidity.E.FOO, EnumValidity.E.BAZ};
+ // E2 contains some invalid values
+ m.repeatedE2 = new int[] {invalid, EnumValidity.E.BAR, alsoInvalid};
+ m.packedE2 = new int[] {EnumValidity.E.FOO, invalid, alsoInvalid};
+ // E3 contains all invalid values
+ m.repeatedE3 = new int[] {invalid, invalid};
+ m.packedE3 = new int[] {alsoInvalid, alsoInvalid};
+ byte[] serialized = MessageNano.toByteArray(m);
+ // Sanity check that we do have all data in the byte array.
+ assertEquals(31, serialized.length);
+
+ // Test 1: tests that invalid values aren't included in the deserialized message.
+ EnumValidity.M deserialized = MessageNano.mergeFrom(new EnumValidity.M(), serialized);
+ assertEquals(EnumValidity.E.default_, deserialized.optionalE);
+ assertEquals(EnumValidity.E.BAZ, deserialized.defaultE);
+ assertTrue(Arrays.equals(
+ new int[] {EnumValidity.E.FOO, EnumValidity.E.BAR}, deserialized.repeatedE));
+ assertTrue(Arrays.equals(
+ new int[] {EnumValidity.E.FOO, EnumValidity.E.BAZ}, deserialized.packedE));
+ assertTrue(Arrays.equals(
+ new int[] {EnumValidity.E.BAR}, deserialized.repeatedE2));
+ assertTrue(Arrays.equals(
+ new int[] {EnumValidity.E.FOO}, deserialized.packedE2));
+ assertEquals(0, deserialized.repeatedE3.length);
+ assertEquals(0, deserialized.packedE3.length);
+
+ // Test 2: tests that invalid values do not override previous values in the field, including
+ // arrays, including pre-existing invalid values.
+ deserialized.optionalE = EnumValidity.E.BAR;
+ deserialized.defaultE = alsoInvalid;
+ deserialized.repeatedE = new int[] {EnumValidity.E.BAZ};
+ deserialized.packedE = new int[] {EnumValidity.E.BAZ, alsoInvalid};
+ deserialized.repeatedE2 = new int[] {invalid, alsoInvalid};
+ deserialized.packedE2 = null;
+ deserialized.repeatedE3 = null;
+ deserialized.packedE3 = new int[0];
+ MessageNano.mergeFrom(deserialized, serialized);
+ assertEquals(EnumValidity.E.BAR, deserialized.optionalE);
+ assertEquals(alsoInvalid, deserialized.defaultE);
+ assertTrue(Arrays.equals(
+ new int[] {EnumValidity.E.BAZ, /* + */ EnumValidity.E.FOO, EnumValidity.E.BAR},
+ deserialized.repeatedE));
+ assertTrue(Arrays.equals(
+ new int[] {EnumValidity.E.BAZ, alsoInvalid, /* + */ EnumValidity.E.FOO, EnumValidity.E.BAZ},
+ deserialized.packedE));
+ assertTrue(Arrays.equals(
+ new int[] {invalid, alsoInvalid, /* + */ EnumValidity.E.BAR},
+ deserialized.repeatedE2));
+ assertTrue(Arrays.equals(
+ new int[] {/* <null> + */ EnumValidity.E.FOO},
+ deserialized.packedE2));
+ assertNull(deserialized.repeatedE3); // null + all invalid == null
+ assertEquals(0, deserialized.packedE3.length); // empty + all invalid == empty
+
+ // Test 3: reading by alternative forms
+ EnumValidity.Alt alt = MessageNano.mergeFrom(new EnumValidity.Alt(), serialized);
+ assertEquals(EnumValidity.E.BAR, // last valid value in m.repeatedE2
+ alt.repeatedE2AsOptional);
+ assertTrue(Arrays.equals(new int[] {EnumValidity.E.FOO}, alt.packedE2AsNonPacked));
+ assertEquals(0, alt.nonPackedE3AsPacked.length);
+ }
+
+ /**
+ * Tests the same as {@link #testNanoEnumValidity()} with accessor style. Repeated fields are
+ * not re-tested here because they are not affected by the accessor style.
+ */
+ public void testNanoEnumValidityAccessors() throws Exception {
+ final int invalid = 120;
+ final int alsoInvalid = 121;
+
+ EnumValidityAccessors.M m = new EnumValidityAccessors.M();
+ // Sanity check & baseline of the assertions for the first case below.
+ assertEquals(EnumValidityAccessors.default_, m.getOptionalE());
+ assertEquals(EnumValidityAccessors.BAZ, m.getDefaultE());
+
+ m.setOptionalE(invalid);
+ m.setDefaultE(invalid);
+ // Set repeatedE2 for Alt.repeatedE2AsOptional
+ m.repeatedE2 = new int[] {invalid, EnumValidityAccessors.BAR, alsoInvalid};
+ byte[] serialized = MessageNano.toByteArray(m);
+ // Sanity check that we do have all data in the byte array.
+ assertEquals(10, serialized.length);
+
+ // Test 1: tests that invalid values aren't included in the deserialized message.
+ EnumValidityAccessors.M deserialized =
+ MessageNano.mergeFrom(new EnumValidityAccessors.M(), serialized);
+ assertEquals(EnumValidityAccessors.default_, deserialized.getOptionalE());
+ assertEquals(EnumValidityAccessors.BAZ, deserialized.getDefaultE());
+
+ // Test 2: tests that invalid values do not override previous values in the field, including
+ // pre-existing invalid values.
+ deserialized.setOptionalE(EnumValidityAccessors.BAR);
+ deserialized.setDefaultE(alsoInvalid);
+ MessageNano.mergeFrom(deserialized, serialized);
+ assertEquals(EnumValidityAccessors.BAR, deserialized.getOptionalE());
+ assertEquals(alsoInvalid, deserialized.getDefaultE());
+
+ // Test 3: reading by alternative forms
+ EnumValidityAccessors.Alt alt =
+ MessageNano.mergeFrom(new EnumValidityAccessors.Alt(), serialized);
+ assertEquals(EnumValidityAccessors.BAR, // last valid value in m.repeatedE2
+ alt.getRepeatedE2AsOptional());
+ }
+
+ /**
* Tests that code generation correctly wraps a single message into its outer
* class. The class {@code SingleMessageNano} is imported from the outer
* class {@code UnittestSingleNano}, whose name is implicit. Any error would
@@ -2204,6 +2326,7 @@ public class NanoTest extends TestCase {
assertFalse(msg.hasDefaultFloatNan);
assertFalse(msg.hasDefaultNestedEnum);
assertFalse(msg.hasId);
+ assertFalse(msg.hasRequiredEnum);
msg.optionalInt32 = 123;
msg.optionalNestedMessage = new TestAllTypesNanoHas.NestedMessage();
msg.optionalNestedMessage.bb = 2;
@@ -2213,7 +2336,7 @@ public class NanoTest extends TestCase {
byte [] result = MessageNano.toByteArray(msg);
int msgSerializedSize = msg.getSerializedSize();
//System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length);
- assertTrue(msgSerializedSize == 13);
+ assertTrue(msgSerializedSize == 10);
assertEquals(result.length, msgSerializedSize);
// Has fields true upon parse.
@@ -2239,6 +2362,8 @@ public class NanoTest extends TestCase {
msg.hasDefaultBytes = true;
msg.hasDefaultFloatNan = true;
msg.hasDefaultNestedEnum = true;
+ msg.hasId = true;
+ msg.hasRequiredEnum = true;
byte [] result = MessageNano.toByteArray(msg);
int msgSerializedSize = msg.getSerializedSize();
@@ -2257,6 +2382,7 @@ public class NanoTest extends TestCase {
assertTrue(newMsg.hasDefaultFloatNan);
assertTrue(newMsg.hasDefaultNestedEnum);
assertTrue(newMsg.hasId);
+ assertTrue(newMsg.hasRequiredEnum);
assertEquals(0, newMsg.optionalInt32);
assertEquals(0, newMsg.optionalString.length());
assertEquals(0, newMsg.optionalBytes.length);
@@ -2268,6 +2394,7 @@ public class NanoTest extends TestCase {
assertEquals(TestAllTypesNanoHas.BAR, newMsg.defaultNestedEnum);
assertEquals(Float.NaN, newMsg.defaultFloatNan);
assertEquals(0, newMsg.id);
+ assertEquals(TestAllTypesNanoHas.FOO, newMsg.requiredEnum);
}
public void testNanoWithAccessorsBasic() throws Exception {
@@ -2991,13 +3118,6 @@ public class NanoTest extends TestCase {
TestAllTypesNano.BAR,
TestAllTypesNano.BAZ
};
- // We set the _nan fields to something other than nan, because equality
- // is defined for nan such that Float.NaN != Float.NaN, which makes any
- // instance of TestAllTypesNano unequal to any other instance unless
- // these fields are set. This is also the behavior of the regular java
- // generator when the value of a field is NaN.
- message.defaultFloatNan = 1.0f;
- message.defaultDoubleNan = 1.0;
return message;
}
@@ -3020,7 +3140,6 @@ public class NanoTest extends TestCase {
TestAllTypesNano.BAR,
TestAllTypesNano.BAZ
};
- message.defaultFloatNan = 1.0f;
return message;
}
@@ -3029,8 +3148,7 @@ public class NanoTest extends TestCase {
.setOptionalInt32(5)
.setOptionalString("Hello")
.setOptionalBytes(new byte[] {1, 2, 3})
- .setOptionalNestedEnum(TestNanoAccessors.BAR)
- .setDefaultFloatNan(1.0f);
+ .setOptionalNestedEnum(TestNanoAccessors.BAR);
message.optionalNestedMessage = new TestNanoAccessors.NestedMessage().setBb(27);
message.repeatedInt32 = new int[] { 5, 6, 7, 8 };
message.repeatedString = new String[] { "One", "Two" };
@@ -3078,6 +3196,126 @@ public class NanoTest extends TestCase {
return message;
}
+ public void testEqualsWithSpecialFloatingPointValues() throws Exception {
+ // Checks that the nano implementation complies with Object.equals() when treating
+ // floating point numbers, i.e. NaN == NaN and +0.0 != -0.0.
+ // This test assumes that the generated equals() implementations are symmetric, so
+ // there will only be one direction for each equality check.
+
+ TestAllTypesNano m1 = new TestAllTypesNano();
+ m1.optionalFloat = Float.NaN;
+ m1.optionalDouble = Double.NaN;
+ TestAllTypesNano m2 = new TestAllTypesNano();
+ m2.optionalFloat = Float.NaN;
+ m2.optionalDouble = Double.NaN;
+ assertTrue(m1.equals(m2));
+ assertTrue(m1.equals(
+ MessageNano.mergeFrom(new TestAllTypesNano(), MessageNano.toByteArray(m1))));
+
+ m1.optionalFloat = +0f;
+ m2.optionalFloat = -0f;
+ assertFalse(m1.equals(m2));
+
+ m1.optionalFloat = -0f;
+ m1.optionalDouble = +0d;
+ m2.optionalDouble = -0d;
+ assertFalse(m1.equals(m2));
+
+ m1.optionalDouble = -0d;
+ assertTrue(m1.equals(m2));
+ assertFalse(m1.equals(new TestAllTypesNano())); // -0 does not equals() the default +0
+ assertTrue(m1.equals(
+ MessageNano.mergeFrom(new TestAllTypesNano(), MessageNano.toByteArray(m1))));
+
+ // -------
+
+ TestAllTypesNanoHas m3 = new TestAllTypesNanoHas();
+ m3.optionalFloat = Float.NaN;
+ m3.hasOptionalFloat = true;
+ m3.optionalDouble = Double.NaN;
+ m3.hasOptionalDouble = true;
+ TestAllTypesNanoHas m4 = new TestAllTypesNanoHas();
+ m4.optionalFloat = Float.NaN;
+ m4.hasOptionalFloat = true;
+ m4.optionalDouble = Double.NaN;
+ m4.hasOptionalDouble = true;
+ assertTrue(m3.equals(m4));
+ assertTrue(m3.equals(
+ MessageNano.mergeFrom(new TestAllTypesNanoHas(), MessageNano.toByteArray(m3))));
+
+ m3.optionalFloat = +0f;
+ m4.optionalFloat = -0f;
+ assertFalse(m3.equals(m4));
+
+ m3.optionalFloat = -0f;
+ m3.optionalDouble = +0d;
+ m4.optionalDouble = -0d;
+ assertFalse(m3.equals(m4));
+
+ m3.optionalDouble = -0d;
+ m3.hasOptionalFloat = false; // -0 does not equals() the default +0,
+ m3.hasOptionalDouble = false; // so these incorrect 'has' flags should be disregarded.
+ assertTrue(m3.equals(m4)); // note: m4 has the 'has' flags set.
+ assertFalse(m3.equals(new TestAllTypesNanoHas())); // note: the new message has +0 defaults
+ assertTrue(m3.equals(
+ MessageNano.mergeFrom(new TestAllTypesNanoHas(), MessageNano.toByteArray(m3))));
+ // note: the deserialized message has the 'has' flags set.
+
+ // -------
+
+ TestNanoAccessors m5 = new TestNanoAccessors();
+ m5.setOptionalFloat(Float.NaN);
+ m5.setOptionalDouble(Double.NaN);
+ TestNanoAccessors m6 = new TestNanoAccessors();
+ m6.setOptionalFloat(Float.NaN);
+ m6.setOptionalDouble(Double.NaN);
+ assertTrue(m5.equals(m6));
+ assertTrue(m5.equals(
+ MessageNano.mergeFrom(new TestNanoAccessors(), MessageNano.toByteArray(m6))));
+
+ m5.setOptionalFloat(+0f);
+ m6.setOptionalFloat(-0f);
+ assertFalse(m5.equals(m6));
+
+ m5.setOptionalFloat(-0f);
+ m5.setOptionalDouble(+0d);
+ m6.setOptionalDouble(-0d);
+ assertFalse(m5.equals(m6));
+
+ m5.setOptionalDouble(-0d);
+ assertTrue(m5.equals(m6));
+ assertFalse(m5.equals(new TestNanoAccessors()));
+ assertTrue(m5.equals(
+ MessageNano.mergeFrom(new TestNanoAccessors(), MessageNano.toByteArray(m6))));
+
+ // -------
+
+ NanoReferenceTypes.TestAllTypesNano m7 = new NanoReferenceTypes.TestAllTypesNano();
+ m7.optionalFloat = Float.NaN;
+ m7.optionalDouble = Double.NaN;
+ NanoReferenceTypes.TestAllTypesNano m8 = new NanoReferenceTypes.TestAllTypesNano();
+ m8.optionalFloat = Float.NaN;
+ m8.optionalDouble = Double.NaN;
+ assertTrue(m7.equals(m8));
+ assertTrue(m7.equals(MessageNano.mergeFrom(
+ new NanoReferenceTypes.TestAllTypesNano(), MessageNano.toByteArray(m7))));
+
+ m7.optionalFloat = +0f;
+ m8.optionalFloat = -0f;
+ assertFalse(m7.equals(m8));
+
+ m7.optionalFloat = -0f;
+ m7.optionalDouble = +0d;
+ m8.optionalDouble = -0d;
+ assertFalse(m7.equals(m8));
+
+ m7.optionalDouble = -0d;
+ assertTrue(m7.equals(m8));
+ assertFalse(m7.equals(new NanoReferenceTypes.TestAllTypesNano()));
+ assertTrue(m7.equals(MessageNano.mergeFrom(
+ new NanoReferenceTypes.TestAllTypesNano(), MessageNano.toByteArray(m7))));
+ }
+
public void testNullRepeatedFields() throws Exception {
// Check that serialization after explicitly setting a repeated field
// to null doesn't NPE.
@@ -3242,12 +3480,16 @@ public class NanoTest extends TestCase {
// Check that repeated fields with packable types can accept both packed and unpacked
// serialized forms.
NanoRepeatedPackables.NonPacked nonPacked = new NanoRepeatedPackables.NonPacked();
- nonPacked.int32S = new int[] {1, 2, 3};
- nonPacked.int64S = new long[] {4, 5, 6};
- nonPacked.uint32S = new int[] {7, 8, 9};
- nonPacked.uint64S = new long[] {10, 11, 12};
- nonPacked.sint32S = new int[] {13, 14, 15};
- nonPacked.sint64S = new long[] {16, 17, 18};
+ // Exaggerates the first values of varint-typed arrays. This is to test that the parsing code
+ // of packed fields handles non-packed data correctly. If the code incorrectly thinks it is
+ // reading from a packed tag, it will read the first value as the byte length of the field,
+ // and the large number will cause the input to go out of bounds, thus capturing the error.
+ nonPacked.int32S = new int[] {1000, 2, 3};
+ nonPacked.int64S = new long[] {4000, 5, 6};
+ nonPacked.uint32S = new int[] {7000, 8, 9};
+ nonPacked.uint64S = new long[] {10000, 11, 12};
+ nonPacked.sint32S = new int[] {13000, 14, 15};
+ nonPacked.sint64S = new long[] {16000, 17, 18};
nonPacked.fixed32S = new int[] {19, 20, 21};
nonPacked.fixed64S = new long[] {22, 23, 24};
nonPacked.sfixed32S = new int[] {25, 26, 27};
@@ -3284,8 +3526,8 @@ public class NanoTest extends TestCase {
nonPacked = MessageNano.mergeFrom(new NanoRepeatedPackables.NonPacked(), mixedSerialized);
packed = MessageNano.mergeFrom(new NanoRepeatedPackables.Packed(), mixedSerialized);
assertRepeatedPackablesEqual(nonPacked, packed);
- assertTrue(Arrays.equals(new int[] {1, 2, 3, 1, 2, 3}, nonPacked.int32S));
- assertTrue(Arrays.equals(new int[] {13, 14, 15, 13, 14, 15}, nonPacked.sint32S));
+ assertTrue(Arrays.equals(new int[] {1000, 2, 3, 1000, 2, 3}, nonPacked.int32S));
+ assertTrue(Arrays.equals(new int[] {13000, 14, 15, 13000, 14, 15}, nonPacked.sint32S));
assertTrue(Arrays.equals(new int[] {25, 26, 27, 25, 26, 27}, nonPacked.sfixed32S));
assertTrue(Arrays.equals(new boolean[] {false, true, false, true}, nonPacked.bools));
}
diff --git a/src/google/protobuf/compiler/javanano/javanano_enum_field.cc b/src/google/protobuf/compiler/javanano/javanano_enum_field.cc
index 5d3511b..6a18834 100644
--- a/src/google/protobuf/compiler/javanano/javanano_enum_field.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_enum_field.cc
@@ -71,9 +71,35 @@ void SetEnumVariables(const Params& params,
(*variables)["tag"] = SimpleItoa(internal::WireFormat::MakeTag(descriptor));
(*variables)["tag_size"] = SimpleItoa(
internal::WireFormat::TagSize(descriptor->number(), descriptor->type()));
+ (*variables)["non_packed_tag"] = SimpleItoa(
+ internal::WireFormatLite::MakeTag(descriptor->number(),
+ internal::WireFormat::WireTypeForFieldType(descriptor->type())));
(*variables)["message_name"] = descriptor->containing_type()->name();
}
+void LoadEnumValues(const Params& params,
+ const EnumDescriptor* enum_descriptor, vector<string>* canonical_values) {
+ string enum_class_name = ClassName(params, enum_descriptor);
+ for (int i = 0; i < enum_descriptor->value_count(); i++) {
+ const EnumValueDescriptor* value = enum_descriptor->value(i);
+ const EnumValueDescriptor* canonical_value =
+ enum_descriptor->FindValueByNumber(value->number());
+ if (value == canonical_value) {
+ canonical_values->push_back(
+ enum_class_name + "." + RenameJavaKeywords(value->name()));
+ }
+ }
+}
+
+void PrintCaseLabels(
+ io::Printer* printer, const vector<string>& canonical_values) {
+ for (int i = 0; i < canonical_values.size(); i++) {
+ printer->Print(
+ " case $value$:\n",
+ "value", canonical_values[i]);
+ }
+}
+
} // namespace
// ===================================================================
@@ -82,6 +108,7 @@ EnumFieldGenerator::
EnumFieldGenerator(const FieldDescriptor* descriptor, const Params& params)
: FieldGenerator(params), descriptor_(descriptor) {
SetEnumVariables(params, descriptor, &variables_);
+ LoadEnumValues(params, descriptor->enum_type(), &canonical_values_);
}
EnumFieldGenerator::~EnumFieldGenerator() {}
@@ -111,17 +138,27 @@ GenerateClearCode(io::Printer* printer) const {
void EnumFieldGenerator::
GenerateMergingCode(io::Printer* printer) const {
printer->Print(variables_,
- "this.$name$ = input.readInt32();\n");
-
+ "int value = input.readInt32();\n"
+ "switch (value) {\n");
+ PrintCaseLabels(printer, canonical_values_);
+ printer->Print(variables_,
+ " this.$name$ = value;\n");
if (params_.generate_has()) {
printer->Print(variables_,
- "has$capitalized_name$ = true;\n");
+ " has$capitalized_name$ = true;\n");
}
+ printer->Print(
+ " break;\n"
+ "}\n");
+ // No default case: in case of invalid value from the wire, preserve old
+ // field value. Also we are not storing the invalid value into the unknown
+ // fields, because there is no way to get the value out.
}
void EnumFieldGenerator::
GenerateSerializationCode(io::Printer* printer) const {
- if (descriptor_->is_required()) {
+ if (descriptor_->is_required() && !params_.generate_has()) {
+ // Always serialize a required field if we don't have the 'has' signal.
printer->Print(variables_,
"output.writeInt32($number$, this.$name$);\n");
} else {
@@ -140,7 +177,7 @@ GenerateSerializationCode(io::Printer* printer) const {
void EnumFieldGenerator::
GenerateSerializedSizeCode(io::Printer* printer) const {
- if (descriptor_->is_required()) {
+ if (descriptor_->is_required() && !params_.generate_has()) {
printer->Print(variables_,
"size += com.google.protobuf.nano.CodedOutputByteBufferNano\n"
" .computeInt32Size($number$, this.$name$);\n");
@@ -208,6 +245,7 @@ AccessorEnumFieldGenerator(const FieldDescriptor* descriptor,
const Params& params, int has_bit_index)
: FieldGenerator(params), descriptor_(descriptor) {
SetEnumVariables(params, descriptor, &variables_);
+ LoadEnumValues(params, descriptor->enum_type(), &canonical_values_);
SetBitOperationVariables("has", has_bit_index, &variables_);
}
@@ -244,8 +282,17 @@ GenerateClearCode(io::Printer* printer) const {
void AccessorEnumFieldGenerator::
GenerateMergingCode(io::Printer* printer) const {
printer->Print(variables_,
- "$name$_ = input.readInt32();\n"
- "$set_has$;\n");
+ "int value = input.readInt32();\n"
+ "switch (value) {\n");
+ PrintCaseLabels(printer, canonical_values_);
+ printer->Print(variables_,
+ " $name$_ = value;\n"
+ " $set_has$;\n"
+ " break;\n"
+ "}\n");
+ // No default case: in case of invalid value from the wire, preserve old
+ // field value. Also we are not storing the invalid value into the unknown
+ // fields, because there is no way to get the value out.
}
void AccessorEnumFieldGenerator::
@@ -286,6 +333,7 @@ RepeatedEnumFieldGenerator::
RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor, const Params& params)
: FieldGenerator(params), descriptor_(descriptor) {
SetEnumVariables(params, descriptor, &variables_);
+ LoadEnumValues(params, descriptor->enum_type(), &canonical_values_);
}
RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {}
@@ -304,46 +352,82 @@ GenerateClearCode(io::Printer* printer) const {
void RepeatedEnumFieldGenerator::
GenerateMergingCode(io::Printer* printer) const {
- // First, figure out the length of the array, then parse.
+ // First, figure out the maximum length of the array, then parse,
+ // and finally copy the valid values to the field.
printer->Print(variables_,
- "int arrayLength = com.google.protobuf.nano.WireFormatNano\n"
- " .getRepeatedFieldArrayLength(input, $tag$);\n"
- "int i = this.$name$ == null ? 0 : this.$name$.length;\n"
- "int[] newArray = new int[i + arrayLength];\n"
- "if (i != 0) {\n"
- " java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n"
- "}\n"
- "for (; i < newArray.length - 1; i++) {\n"
- " newArray[i] = input.readInt32();\n"
- " input.readTag();\n"
+ "int length = com.google.protobuf.nano.WireFormatNano\n"
+ " .getRepeatedFieldArrayLength(input, $non_packed_tag$);\n"
+ "int[] validValues = new int[length];\n"
+ "int validCount = 0;\n"
+ "for (int i = 0; i < length; i++) {\n"
+ " if (i != 0) { // tag for first value already consumed.\n"
+ " input.readTag();\n"
+ " }\n"
+ " int value = input.readInt32();\n"
+ " switch (value) {\n");
+ printer->Indent();
+ PrintCaseLabels(printer, canonical_values_);
+ printer->Outdent();
+ printer->Print(variables_,
+ " validValues[validCount++] = value;\n"
+ " break;\n"
+ " }\n"
"}\n"
- "// Last one without readTag.\n"
- "newArray[i] = input.readInt32();\n"
- "this.$name$ = newArray;\n");
+ "if (validCount != 0) {\n"
+ " int i = this.$name$ == null ? 0 : this.$name$.length;\n"
+ " if (i == 0 && validCount == validValues.length) {\n"
+ " this.$name$ = validValues;\n"
+ " } else {\n"
+ " int[] newArray = new int[i + validCount];\n"
+ " if (i != 0) {\n"
+ " java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n"
+ " }\n"
+ " java.lang.System.arraycopy(validValues, 0, newArray, i, validCount);\n"
+ " this.$name$ = newArray;\n"
+ " }\n"
+ "}\n");
}
void RepeatedEnumFieldGenerator::
GenerateMergingCodeFromPacked(io::Printer* printer) const {
printer->Print(variables_,
- "int length = input.readRawVarint32();\n"
- "int limit = input.pushLimit(length);\n"
+ "int bytes = input.readRawVarint32();\n"
+ "int limit = input.pushLimit(bytes);\n"
"// First pass to compute array length.\n"
"int arrayLength = 0;\n"
"int startPos = input.getPosition();\n"
"while (input.getBytesUntilLimit() > 0) {\n"
- " input.readInt32();\n"
- " arrayLength++;\n"
- "}\n"
- "input.rewindToPosition(startPos);\n"
- "int i = this.$name$ == null ? 0 : this.$name$.length;\n"
- "int[] newArray = new int[i + arrayLength];\n"
- "if (i != 0) {\n"
- " java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n"
+ " switch (input.readInt32()) {\n");
+ printer->Indent();
+ PrintCaseLabels(printer, canonical_values_);
+ printer->Outdent();
+ printer->Print(variables_,
+ " arrayLength++;\n"
+ " break;\n"
+ " }\n"
"}\n"
- "for (; i < newArray.length; i++) {\n"
- " newArray[i] = input.readInt32();\n"
+ "if (arrayLength != 0) {\n"
+ " input.rewindToPosition(startPos);\n"
+ " int i = this.$name$ == null ? 0 : this.$name$.length;\n"
+ " int[] newArray = new int[i + arrayLength];\n"
+ " if (i != 0) {\n"
+ " java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n"
+ " }\n"
+ " while (input.getBytesUntilLimit() > 0) {\n"
+ " int value = input.readInt32();\n"
+ " switch (value) {\n");
+ printer->Indent();
+ printer->Indent();
+ PrintCaseLabels(printer, canonical_values_);
+ printer->Outdent();
+ printer->Outdent();
+ printer->Print(variables_,
+ " newArray[i++] = value;\n"
+ " break;\n"
+ " }\n"
+ " }\n"
+ " this.$name$ = newArray;\n"
"}\n"
- "this.$name$ = newArray;\n"
"input.popLimit(limit);\n");
}
diff --git a/src/google/protobuf/compiler/javanano/javanano_enum_field.h b/src/google/protobuf/compiler/javanano/javanano_enum_field.h
index c477af2..55bf635 100644
--- a/src/google/protobuf/compiler/javanano/javanano_enum_field.h
+++ b/src/google/protobuf/compiler/javanano/javanano_enum_field.h
@@ -37,6 +37,7 @@
#include <map>
#include <string>
+#include <vector>
#include <google/protobuf/compiler/javanano/javanano_field.h>
namespace google {
@@ -62,6 +63,7 @@ class EnumFieldGenerator : public FieldGenerator {
private:
const FieldDescriptor* descriptor_;
map<string, string> variables_;
+ vector<string> canonical_values_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumFieldGenerator);
};
@@ -84,6 +86,7 @@ class AccessorEnumFieldGenerator : public FieldGenerator {
private:
const FieldDescriptor* descriptor_;
map<string, string> variables_;
+ vector<string> canonical_values_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(AccessorEnumFieldGenerator);
};
@@ -109,6 +112,7 @@ class RepeatedEnumFieldGenerator : public FieldGenerator {
const FieldDescriptor* descriptor_;
map<string, string> variables_;
+ vector<string> canonical_values_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedEnumFieldGenerator);
};
diff --git a/src/google/protobuf/compiler/javanano/javanano_generator.cc b/src/google/protobuf/compiler/javanano/javanano_generator.cc
index 5c8bcb6..d514dd8 100644
--- a/src/google/protobuf/compiler/javanano/javanano_generator.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_generator.cc
@@ -46,6 +46,19 @@ namespace protobuf {
namespace compiler {
namespace javanano {
+namespace {
+
+string TrimString(const string& s) {
+ string::size_type start = s.find_first_not_of(" \n\r\t");
+ if (start == string::npos) {
+ return "";
+ }
+ string::size_type end = s.find_last_not_of(" \n\r\t") + 1;
+ return s.substr(start, end - start);
+}
+
+} // namespace
+
void UpdateParamsRecursively(Params& params,
const FileDescriptor* file) {
// Add any parameters for this file
@@ -93,42 +106,44 @@ bool JavaNanoGenerator::Generate(const FileDescriptor* file,
// Replace any existing options with ones from command line
for (int i = 0; i < options.size(); i++) {
- if (options[i].first == "output_list_file") {
- output_list_file = options[i].second;
- } else if (options[i].first == "java_package") {
+ string option_name = TrimString(options[i].first);
+ string option_value = TrimString(options[i].second);
+ if (option_name == "output_list_file") {
+ output_list_file = option_value;
+ } else if (option_name == "java_package") {
vector<string> parts;
- SplitStringUsing(options[i].second, "|", &parts);
+ SplitStringUsing(option_value, "|", &parts);
if (parts.size() != 2) {
*error = "Bad java_package, expecting filename|PackageName found '"
- + options[i].second + "'";
+ + option_value + "'";
return false;
}
params.set_java_package(parts[0], parts[1]);
- } else if (options[i].first == "java_outer_classname") {
+ } else if (option_name == "java_outer_classname") {
vector<string> parts;
- SplitStringUsing(options[i].second, "|", &parts);
+ SplitStringUsing(option_value, "|", &parts);
if (parts.size() != 2) {
*error = "Bad java_outer_classname, "
"expecting filename|ClassName found '"
- + options[i].second + "'";
+ + option_value + "'";
return false;
}
params.set_java_outer_classname(parts[0], parts[1]);
- } else if (options[i].first == "store_unknown_fields") {
- 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 if (options[i].first == "enum_style") {
- params.set_java_enum_style(options[i].second == "java");
- } else if (options[i].first == "optional_field_style") {
- params.set_optional_field_accessors(options[i].second == "accessors");
- params.set_use_reference_types_for_primitives(options[i].second == "reftypes");
- } else if (options[i].first == "generate_equals") {
- params.set_generate_equals(options[i].second == "true");
+ } else if (option_name == "store_unknown_fields") {
+ params.set_store_unknown_fields(option_value == "true");
+ } else if (option_name == "java_multiple_files") {
+ params.set_override_java_multiple_files(option_value == "true");
+ } else if (option_name == "java_nano_generate_has") {
+ params.set_generate_has(option_value == "true");
+ } else if (option_name == "enum_style") {
+ params.set_java_enum_style(option_value == "java");
+ } else if (option_name == "optional_field_style") {
+ params.set_optional_field_accessors(option_value == "accessors");
+ params.set_use_reference_types_for_primitives(option_value == "reftypes");
+ } else if (option_name == "generate_equals") {
+ params.set_generate_equals(option_value == "true");
} else {
- *error = "Ignore unknown javanano generator option: " + options[i].first;
+ *error = "Ignore unknown javanano generator option: " + option_name;
}
}
diff --git a/src/google/protobuf/compiler/javanano/javanano_helpers.cc b/src/google/protobuf/compiler/javanano/javanano_helpers.cc
index 893cdde..e8326a4 100644
--- a/src/google/protobuf/compiler/javanano/javanano_helpers.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_helpers.cc
@@ -448,7 +448,7 @@ string DefaultValue(const Params& params, const FieldDescriptor* field) {
case FieldDescriptor::CPPTYPE_ENUM:
return ClassName(params, field->enum_type()) + "." +
- field->default_value_enum()->name();
+ RenameJavaKeywords(field->default_value_enum()->name());
case FieldDescriptor::CPPTYPE_MESSAGE:
return "null";
diff --git a/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc b/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc
index 06c0b8b..a3bc3a8 100644
--- a/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc
@@ -155,38 +155,6 @@ int FixedSize(FieldDescriptor::Type type) {
return -1;
}
-// Returns true if the field has a default value equal to NaN.
-bool IsDefaultNaN(const FieldDescriptor* field) {
- switch (field->type()) {
- case FieldDescriptor::TYPE_INT32 : return false;
- case FieldDescriptor::TYPE_UINT32 : return false;
- case FieldDescriptor::TYPE_SINT32 : return false;
- case FieldDescriptor::TYPE_FIXED32 : return false;
- case FieldDescriptor::TYPE_SFIXED32: return false;
- case FieldDescriptor::TYPE_INT64 : return false;
- case FieldDescriptor::TYPE_UINT64 : return false;
- case FieldDescriptor::TYPE_SINT64 : return false;
- case FieldDescriptor::TYPE_FIXED64 : return false;
- case FieldDescriptor::TYPE_SFIXED64: return false;
- case FieldDescriptor::TYPE_FLOAT :
- return isnan(field->default_value_float());
- case FieldDescriptor::TYPE_DOUBLE :
- return isnan(field->default_value_double());
- case FieldDescriptor::TYPE_BOOL : return false;
- case FieldDescriptor::TYPE_STRING : return false;
- case FieldDescriptor::TYPE_BYTES : return false;
- case FieldDescriptor::TYPE_ENUM : return false;
- case FieldDescriptor::TYPE_GROUP : return false;
- case FieldDescriptor::TYPE_MESSAGE : return false;
-
- // No default because we want the compiler to complain if any new
- // types are added.
- }
-
- GOOGLE_LOG(FATAL) << "Can't get here.";
- return false;
-}
-
// Return true if the type is a that has variable length
// for instance String's.
bool IsVariableLenType(JavaType type) {
@@ -273,6 +241,9 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, const Params param
(*variables)["tag"] = SimpleItoa(WireFormat::MakeTag(descriptor));
(*variables)["tag_size"] = SimpleItoa(
WireFormat::TagSize(descriptor->number(), descriptor->type()));
+ (*variables)["non_packed_tag"] = SimpleItoa(
+ internal::WireFormatLite::MakeTag(descriptor->number(),
+ internal::WireFormat::WireTypeForFieldType(descriptor->type())));
int fixed_size = FixedSize(descriptor->type());
if (fixed_size != -1) {
(*variables)["fixed_size"] = SimpleItoa(fixed_size);
@@ -322,7 +293,7 @@ GenerateMembers(io::Printer* printer, bool lazy_init) const {
if (params_.generate_has()) {
printer->Print(variables_,
- "public boolean has$capitalized_name$ = false;\n");
+ "public boolean has$capitalized_name$;\n");
}
}
@@ -364,15 +335,21 @@ GenerateSerializationConditional(io::Printer* printer) const {
printer->Print(variables_,
"if (");
}
- if (IsArrayType(GetJavaType(descriptor_))) {
+ JavaType java_type = GetJavaType(descriptor_);
+ if (IsArrayType(java_type)) {
printer->Print(variables_,
"!java.util.Arrays.equals(this.$name$, $default$)) {\n");
- } else if (IsReferenceType(GetJavaType(descriptor_))) {
+ } else if (IsReferenceType(java_type)) {
printer->Print(variables_,
"!this.$name$.equals($default$)) {\n");
- } else if (IsDefaultNaN(descriptor_)) {
+ } else if (java_type == JAVATYPE_FLOAT) {
printer->Print(variables_,
- "!$capitalized_type$.isNaN(this.$name$)) {\n");
+ "java.lang.Float.floatToIntBits(this.$name$)\n"
+ " != java.lang.Float.floatToIntBits($default$)) {\n");
+ } else if (java_type == JAVATYPE_DOUBLE) {
+ printer->Print(variables_,
+ "java.lang.Double.doubleToLongBits(this.$name$)\n"
+ " != java.lang.Double.doubleToLongBits($default$)) {\n");
} else {
printer->Print(variables_,
"this.$name$ != $default$) {\n");
@@ -381,7 +358,8 @@ GenerateSerializationConditional(io::Printer* printer) const {
void PrimitiveFieldGenerator::
GenerateSerializationCode(io::Printer* printer) const {
- if (descriptor_->is_required()) {
+ if (descriptor_->is_required() && !params_.generate_has()) {
+ // Always serialize a required field if we don't have the 'has' signal.
printer->Print(variables_,
"output.write$capitalized_type$($number$, this.$name$);\n");
} else {
@@ -394,7 +372,7 @@ GenerateSerializationCode(io::Printer* printer) const {
void PrimitiveFieldGenerator::
GenerateSerializedSizeCode(io::Printer* printer) const {
- if (descriptor_->is_required()) {
+ if (descriptor_->is_required() && !params_.generate_has()) {
printer->Print(variables_,
"size += com.google.protobuf.nano.CodedOutputByteBufferNano\n"
" .compute$capitalized_type$Size($number$, this.$name$);\n");
@@ -443,6 +421,36 @@ GenerateEqualsCode(io::Printer* printer) const {
printer->Print(") {\n"
" return false;\n"
"}\n");
+ } else if (java_type == JAVATYPE_FLOAT) {
+ printer->Print(variables_,
+ "{\n"
+ " int bits = java.lang.Float.floatToIntBits(this.$name$);\n"
+ " if (bits != java.lang.Float.floatToIntBits(other.$name$)");
+ if (params_.generate_has()) {
+ printer->Print(variables_,
+ "\n"
+ " || (bits == java.lang.Float.floatToIntBits($default$)\n"
+ " && this.has$capitalized_name$ != other.has$capitalized_name$)");
+ }
+ printer->Print(") {\n"
+ " return false;\n"
+ " }\n"
+ "}\n");
+ } else if (java_type == JAVATYPE_DOUBLE) {
+ printer->Print(variables_,
+ "{\n"
+ " long bits = java.lang.Double.doubleToLongBits(this.$name$);\n"
+ " if (bits != java.lang.Double.doubleToLongBits(other.$name$)");
+ if (params_.generate_has()) {
+ printer->Print(variables_,
+ "\n"
+ " || (bits == java.lang.Double.doubleToLongBits($default$)\n"
+ " && this.has$capitalized_name$ != other.has$capitalized_name$)");
+ }
+ printer->Print(") {\n"
+ " return false;\n"
+ " }\n"
+ "}\n");
} else {
printer->Print(variables_,
"if (this.$name$ != other.$name$");
@@ -602,12 +610,26 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
void AccessorPrimitiveFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const {
switch (GetJavaType(descriptor_)) {
- // For all Java primitive types below, the hash codes match the
- // results of BoxedType.valueOf(primitiveValue).hashCode().
- case JAVATYPE_INT:
- case JAVATYPE_LONG:
+ // For all Java primitive types below, the equality checks match the
+ // results of BoxedType.valueOf(primitiveValue).equals(otherValue).
case JAVATYPE_FLOAT:
+ printer->Print(variables_,
+ "if ($different_has$\n"
+ " || java.lang.Float.floatToIntBits($name$_)\n"
+ " != java.lang.Float.floatToIntBits(other.$name$_)) {\n"
+ " return false;\n"
+ "}\n");
+ break;
case JAVATYPE_DOUBLE:
+ printer->Print(variables_,
+ "if ($different_has$\n"
+ " || java.lang.Double.doubleToLongBits($name$_)\n"
+ " != java.lang.Double.doubleToLongBits(other.$name$_)) {\n"
+ " return false;\n"
+ "}\n");
+ break;
+ case JAVATYPE_INT:
+ case JAVATYPE_LONG:
case JAVATYPE_BOOLEAN:
printer->Print(variables_,
"if ($different_has$\n"
@@ -709,7 +731,7 @@ GenerateMergingCode(io::Printer* printer) const {
// First, figure out the length of the array, then parse.
printer->Print(variables_,
"int arrayLength = com.google.protobuf.nano.WireFormatNano\n"
- " .getRepeatedFieldArrayLength(input, $tag$);\n"
+ " .getRepeatedFieldArrayLength(input, $non_packed_tag$);\n"
"int i = this.$name$ == null ? 0 : this.$name$.length;\n");
if (GetJavaType(descriptor_) == JAVATYPE_BYTES) {
diff --git a/src/google/protobuf/unittest_accessors_nano.proto b/src/google/protobuf/unittest_accessors_nano.proto
index 875af25..f1d4d34 100644
--- a/src/google/protobuf/unittest_accessors_nano.proto
+++ b/src/google/protobuf/unittest_accessors_nano.proto
@@ -49,6 +49,8 @@ message TestNanoAccessors {
// Singular
optional int32 optional_int32 = 1;
+ optional float optional_float = 11;
+ optional double optional_double = 12;
optional string optional_string = 14;
optional bytes optional_bytes = 15;
diff --git a/src/google/protobuf/unittest_enum_validity_nano.proto b/src/google/protobuf/unittest_enum_validity_nano.proto
new file mode 100644
index 0000000..f7f5742
--- /dev/null
+++ b/src/google/protobuf/unittest_enum_validity_nano.proto
@@ -0,0 +1,28 @@
+package protobuf_unittest;
+
+option java_package = "com.google.protobuf.nano";
+option java_outer_classname = "EnumValidity";
+
+enum E {
+ default = 1; // test java keyword renaming
+ FOO = 2;
+ BAR = 3;
+ BAZ = 4;
+}
+
+message M {
+ optional E optional_e = 1;
+ optional E default_e = 2 [ default = BAZ ];
+ repeated E repeated_e = 3;
+ repeated E packed_e = 4 [ packed = true ];
+ repeated E repeated_e2 = 5;
+ repeated E packed_e2 = 6 [ packed = true ];
+ repeated E repeated_e3 = 7;
+ repeated E packed_e3 = 8 [ packed = true ];
+}
+
+message Alt {
+ optional E repeated_e2_as_optional = 5;
+ repeated E packed_e2_as_non_packed = 6;
+ repeated E non_packed_e3_as_packed = 7 [ packed = true ];
+}
diff --git a/src/google/protobuf/unittest_has_nano.proto b/src/google/protobuf/unittest_has_nano.proto
index 3a1e5b5..289d08a 100644
--- a/src/google/protobuf/unittest_has_nano.proto
+++ b/src/google/protobuf/unittest_has_nano.proto
@@ -49,6 +49,8 @@ message TestAllTypesNanoHas {
// Singular
optional int32 optional_int32 = 1;
+ optional float optional_float = 11;
+ optional double optional_double = 12;
optional string optional_string = 14;
optional bytes optional_bytes = 15;
@@ -75,4 +77,6 @@ message TestAllTypesNanoHas {
optional NestedEnum default_nested_enum = 81 [default = BAR];
required int32 id = 86;
+ required NestedEnum required_enum = 87;
+
}