diff options
author | Max Cai <maxtroy@google.com> | 2014-01-15 18:47:56 +0000 |
---|---|---|
committer | Max Cai <maxtroy@google.com> | 2014-01-16 12:41:15 +0000 |
commit | d888895a3b5cf764856d3a94ed526bf9994c1800 (patch) | |
tree | 813ff16316503cf10e22e894e99debd9ed812317 /java | |
parent | a8af729b5ef822971f025a7e8ff197545986910d (diff) | |
download | external_protobuf-d888895a3b5cf764856d3a94ed526bf9994c1800.zip external_protobuf-d888895a3b5cf764856d3a94ed526bf9994c1800.tar.gz external_protobuf-d888895a3b5cf764856d3a94ed526bf9994c1800.tar.bz2 |
Add validation when parsing enum fields.
Invalid values from the wire are silently ignored.
Unlike full/lite, the invalid values are not stored into the
unknown fields, because there's no way to get them out from
Nano's unknown fields without a matching Extension.
Edited README and slightly moved it towards a standalone
section for Nano, independent of the Micro section.
Change-Id: I2c1eb07f4d6d8f3aea242b8ddd95b9c966f3f177
Diffstat (limited to 'java')
-rw-r--r-- | java/README.txt | 38 | ||||
-rw-r--r-- | java/pom.xml | 10 | ||||
-rw-r--r-- | java/src/test/java/com/google/protobuf/NanoTest.java | 122 |
3 files changed, 151 insertions, 19 deletions
diff --git a/java/README.txt b/java/README.txt index 0646c23..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 diff --git a/java/pom.xml b/java/pom.xml index d7ea4d1..ba281ff 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -176,6 +176,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 687bc16..2a56fa8 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; @@ -2093,6 +2095,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 |