diff options
-rw-r--r-- | Android.mk | 4 | ||||
-rw-r--r-- | java/README.txt | 51 | ||||
-rw-r--r-- | java/pom.xml | 16 | ||||
-rw-r--r-- | java/src/test/java/com/google/protobuf/NanoTest.java | 280 | ||||
-rw-r--r-- | src/google/protobuf/compiler/javanano/javanano_enum_field.cc | 152 | ||||
-rw-r--r-- | src/google/protobuf/compiler/javanano/javanano_enum_field.h | 4 | ||||
-rw-r--r-- | src/google/protobuf/compiler/javanano/javanano_generator.cc | 59 | ||||
-rw-r--r-- | src/google/protobuf/compiler/javanano/javanano_helpers.cc | 2 | ||||
-rw-r--r-- | src/google/protobuf/compiler/javanano/javanano_primitive_field.cc | 110 | ||||
-rw-r--r-- | src/google/protobuf/unittest_accessors_nano.proto | 2 | ||||
-rw-r--r-- | src/google/protobuf/unittest_enum_validity_nano.proto | 28 | ||||
-rw-r--r-- | src/google/protobuf/unittest_has_nano.proto | 4 |
12 files changed, 564 insertions, 148 deletions
@@ -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; + } |