diff options
27 files changed, 1525 insertions, 298 deletions
@@ -16,6 +16,8 @@ LOCAL_PATH := $(call my-dir) +IGNORED_WARNINGS := -Wno-sign-compare -Wno-unused-parameter -Wno-sign-promo + CC_LITE_SRC_FILES := \ src/google/protobuf/stubs/common.cc \ src/google/protobuf/stubs/once.cc \ @@ -227,7 +229,7 @@ LOCAL_C_INCLUDES := \ # #LOCAL_COPY_HEADERS_TO := $(LOCAL_MODULE) -LOCAL_CFLAGS := -DGOOGLE_PROTOBUF_NO_RTTI +LOCAL_CFLAGS := -DGOOGLE_PROTOBUF_NO_RTTI $(IGNORED_WARNINGS) # These are the minimum versions and don't need to be update. ifeq ($(TARGET_ARCH),arm) @@ -293,7 +295,7 @@ LOCAL_C_INCLUDES := \ # #LOCAL_COPY_HEADERS_TO := $(LOCAL_MODULE) -LOCAL_CFLAGS := -DGOOGLE_PROTOBUF_NO_RTTI +LOCAL_CFLAGS := -DGOOGLE_PROTOBUF_NO_RTTI $(IGNORED_WARNINGS) # These are the minimum versions and don't need to be update. ifeq ($(TARGET_ARCH),arm) @@ -319,7 +321,7 @@ LOCAL_C_INCLUDES := \ external/zlib \ $(LOCAL_PATH)/src -LOCAL_CFLAGS := -frtti +LOCAL_CFLAGS := -frtti $(IGNORED_WARNINGS) LOCAL_SDK_VERSION := 14 LOCAL_NDK_STL_VARIANT := gnustl_static @@ -349,6 +351,8 @@ LOCAL_C_INCLUDES := \ LOCAL_STATIC_LIBRARIES += libz LOCAL_LDLIBS := -lpthread +LOCAL_CFLAGS := $(IGNORED_WARNINGS) + include $(BUILD_HOST_EXECUTABLE) # To test java proto params build rules. diff --git a/java/README.txt b/java/README.txt index 3a004c7..976ec84 100644 --- a/java/README.txt +++ b/java/README.txt @@ -429,6 +429,7 @@ Except: - Similar rename from CodedOutputStreamMicro to CodedOutputByteBufferNano. - Repeated fields are in arrays, not ArrayList or Vector. +- Full support of serializing/deserializing repeated packed fields. - Unset messages/groups are null, not an immutable empty default instance. - Required fields are always serialized. diff --git a/java/pom.xml b/java/pom.xml index dcc1cfa..2f40b98 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -133,7 +133,7 @@ </exec> <!-- java nano --> <exec executable="../src/protoc"> - <arg value="--javanano_out=java_package=google/protobuf/unittest_import_nano.proto|com.google.protobuf.nano,java_outer_classname=google/protobuf/unittest_import_nano.proto|UnittestImportNano:target/generated-test-sources" /> + <arg value="--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" /> @@ -154,13 +154,13 @@ <arg value="../src/google/protobuf/unittest_extension_nano.proto" /> </exec> <exec executable="../src/protoc"> - <arg value="--javanano_out=java_nano_generate_has=true:target/generated-test-sources" /> + <arg value="--javanano_out=java_nano_generate_has=true,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_has_nano.proto" /> </exec> <exec executable="../src/protoc"> - <arg value="--javanano_out=optional_field_style=accessors:target/generated-test-sources" /> + <arg value="--javanano_out=optional_field_style=accessors,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_accessors_nano.proto" /> @@ -171,9 +171,10 @@ <arg value="--proto_path=src/test/java" /> <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" /> </exec> <exec executable="../src/protoc"> - <arg value="--javanano_out=optional_field_style=reftypes:target/generated-test-sources" /> + <arg value="--javanano_out=optional_field_style=reftypes,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_reference_types_nano.proto" /> diff --git a/java/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java b/java/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java new file mode 100644 index 0000000..066774d --- /dev/null +++ b/java/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java @@ -0,0 +1,70 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2013 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf.nano; + +import java.util.ArrayList; +import java.util.List; + +/** + * Base class of those Protocol Buffer messages that need to store unknown fields, + * such as extensions. + */ +public abstract class ExtendableMessageNano extends MessageNano { + /** + * A container for fields unknown to the message, including extensions. Extension fields can + * can be accessed through the {@link getExtension()} and {@link setExtension()} methods. + */ + protected List<UnknownFieldData> unknownFieldData; + + @Override + public int getSerializedSize() { + int size = WireFormatNano.computeWireSize(unknownFieldData); + cachedSize = size; + return size; + } + + /** + * Gets the value stored in the specified extension of this message. + */ + public <T> T getExtension(Extension<T> extension) { + return WireFormatNano.getExtension(extension, unknownFieldData); + } + + /** + * Sets the value of the specified extension of this message. + */ + public <T> void setExtension(Extension<T> extension, T value) { + if (unknownFieldData == null) { + unknownFieldData = new ArrayList<UnknownFieldData>(); + } + WireFormatNano.setExtension(extension, value, unknownFieldData); + } +}
\ No newline at end of file diff --git a/java/src/main/java/com/google/protobuf/nano/InternalNano.java b/java/src/main/java/com/google/protobuf/nano/InternalNano.java index 4930951..ce4a206 100644 --- a/java/src/main/java/com/google/protobuf/nano/InternalNano.java +++ b/java/src/main/java/com/google/protobuf/nano/InternalNano.java @@ -31,6 +31,7 @@ package com.google.protobuf.nano; import java.io.UnsupportedEncodingException; +import java.util.Arrays; /** * The classes contained within are used internally by the Protocol Buffer @@ -40,7 +41,10 @@ import java.io.UnsupportedEncodingException; * * @author kenton@google.com (Kenton Varda) */ -public class InternalNano { +public final class InternalNano { + + private InternalNano() {} + /** * Helper called by generated code to construct default values for string * fields. @@ -69,7 +73,7 @@ public class InternalNano { * converts from the generated string to the string we actually want. The * generated code calls this automatically. */ - public static final String stringDefaultValue(String bytes) { + public static String stringDefaultValue(String bytes) { try { return new String(bytes.getBytes("ISO-8859-1"), "UTF-8"); } catch (UnsupportedEncodingException e) { @@ -88,7 +92,7 @@ public class InternalNano { * In this case we only need the second of the two hacks -- allowing us to * embed raw bytes as a string literal with ISO-8859-1 encoding. */ - public static final byte[] bytesDefaultValue(String bytes) { + public static byte[] bytesDefaultValue(String bytes) { try { return bytes.getBytes("ISO-8859-1"); } catch (UnsupportedEncodingException e) { @@ -103,11 +107,215 @@ public class InternalNano { * Helper function to convert a string into UTF-8 while turning the * UnsupportedEncodingException to a RuntimeException. */ - public static final byte[] copyFromUtf8(final String text) { + public static byte[] copyFromUtf8(final String text) { try { return text.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException("UTF-8 not supported?"); } } + + /** + * Checks repeated int field equality; null-value and 0-length fields are + * considered equal. + */ + public static boolean equals(int[] field1, int[] field2) { + if (field1 == null || field1.length == 0) { + return field2 == null || field2.length == 0; + } else { + return Arrays.equals(field1, field2); + } + } + + /** + * Checks repeated long field equality; null-value and 0-length fields are + * considered equal. + */ + public static boolean equals(long[] field1, long[] field2) { + if (field1 == null || field1.length == 0) { + return field2 == null || field2.length == 0; + } else { + return Arrays.equals(field1, field2); + } + } + + /** + * Checks repeated float field equality; null-value and 0-length fields are + * considered equal. + */ + public static boolean equals(float[] field1, float[] field2) { + if (field1 == null || field1.length == 0) { + return field2 == null || field2.length == 0; + } else { + return Arrays.equals(field1, field2); + } + } + + /** + * Checks repeated double field equality; null-value and 0-length fields are + * considered equal. + */ + public static boolean equals(double[] field1, double[] field2) { + if (field1 == null || field1.length == 0) { + return field2 == null || field2.length == 0; + } else { + return Arrays.equals(field1, field2); + } + } + + /** + * Checks repeated boolean field equality; null-value and 0-length fields are + * considered equal. + */ + public static boolean equals(boolean[] field1, boolean[] field2) { + if (field1 == null || field1.length == 0) { + return field2 == null || field2.length == 0; + } else { + return Arrays.equals(field1, field2); + } + } + + /** + * Checks repeated bytes field equality. Only non-null elements are tested. + * Returns true if the two fields have the same sequence of non-null + * elements. Null-value fields and fields of any length with only null + * elements are considered equal. + */ + public static boolean equals(byte[][] field1, byte[][] field2) { + int index1 = 0; + int length1 = field1 == null ? 0 : field1.length; + int index2 = 0; + int length2 = field2 == null ? 0 : field2.length; + while (true) { + while (index1 < length1 && field1[index1] == null) { + index1++; + } + while (index2 < length2 && field2[index2] == null) { + index2++; + } + boolean atEndOf1 = index1 >= length1; + boolean atEndOf2 = index2 >= length2; + if (atEndOf1 && atEndOf2) { + // no more non-null elements to test in both arrays + return true; + } else if (atEndOf1 != atEndOf2) { + // one of the arrays have extra non-null elements + return false; + } else if (!Arrays.equals(field1[index1], field2[index2])) { + // element mismatch + return false; + } + index1++; + index2++; + } + } + + /** + * Checks repeated string/message field equality. Only non-null elements are + * tested. Returns true if the two fields have the same sequence of non-null + * elements. Null-value fields and fields of any length with only null + * elements are considered equal. + */ + public static boolean equals(Object[] field1, Object[] field2) { + int index1 = 0; + int length1 = field1 == null ? 0 : field1.length; + int index2 = 0; + int length2 = field2 == null ? 0 : field2.length; + while (true) { + while (index1 < length1 && field1[index1] == null) { + index1++; + } + while (index2 < length2 && field2[index2] == null) { + index2++; + } + boolean atEndOf1 = index1 >= length1; + boolean atEndOf2 = index2 >= length2; + if (atEndOf1 && atEndOf2) { + // no more non-null elements to test in both arrays + return true; + } else if (atEndOf1 != atEndOf2) { + // one of the arrays have extra non-null elements + return false; + } else if (!field1[index1].equals(field2[index2])) { + // element mismatch + return false; + } + index1++; + index2++; + } + } + + /** + * Computes the hash code of a repeated int field. Null-value and 0-length + * fields have the same hash code. + */ + public static int hashCode(int[] field) { + return field == null || field.length == 0 ? 0 : Arrays.hashCode(field); + } + + /** + * Computes the hash code of a repeated long field. Null-value and 0-length + * fields have the same hash code. + */ + public static int hashCode(long[] field) { + return field == null || field.length == 0 ? 0 : Arrays.hashCode(field); + } + + /** + * Computes the hash code of a repeated float field. Null-value and 0-length + * fields have the same hash code. + */ + public static int hashCode(float[] field) { + return field == null || field.length == 0 ? 0 : Arrays.hashCode(field); + } + + /** + * Computes the hash code of a repeated double field. Null-value and 0-length + * fields have the same hash code. + */ + public static int hashCode(double[] field) { + return field == null || field.length == 0 ? 0 : Arrays.hashCode(field); + } + + /** + * Computes the hash code of a repeated boolean field. Null-value and 0-length + * fields have the same hash code. + */ + public static int hashCode(boolean[] field) { + return field == null || field.length == 0 ? 0 : Arrays.hashCode(field); + } + + /** + * Computes the hash code of a repeated bytes field. Only the sequence of all + * non-null elements are used in the computation. Null-value fields and fields + * of any length with only null elements have the same hash code. + */ + public static int hashCode(byte[][] field) { + int result = 0; + for (int i = 0, size = field == null ? 0 : field.length; i < size; i++) { + byte[] element = field[i]; + if (element != null) { + result = 31 * result + Arrays.hashCode(element); + } + } + return result; + } + + /** + * Computes the hash code of a repeated string/message field. Only the + * sequence of all non-null elements are used in the computation. Null-value + * fields and fields of any length with only null elements have the same hash + * code. + */ + public static int hashCode(Object[] field) { + int result = 0; + for (int i = 0, size = field == null ? 0 : field.length; i < size; i++) { + Object element = field[i]; + if (element != null) { + result = 31 * result + element.hashCode(); + } + } + return result; + } + } diff --git a/java/src/main/java/com/google/protobuf/nano/MessageNano.java b/java/src/main/java/com/google/protobuf/nano/MessageNano.java index 8d4ec39..0cf8416 100644 --- a/java/src/main/java/com/google/protobuf/nano/MessageNano.java +++ b/java/src/main/java/com/google/protobuf/nano/MessageNano.java @@ -38,6 +38,8 @@ import java.io.IOException; * @author wink@google.com Wink Saville */ public abstract class MessageNano { + protected int cachedSize = -1; + /** * Get the number of bytes required to encode this message. * Returns the cached size or calls getSerializedSize which @@ -45,14 +47,24 @@ public abstract class MessageNano { * so the size is only computed once. If a member is modified * then this could be stale call getSerializedSize if in doubt. */ - abstract public int getCachedSize(); + public int getCachedSize() { + if (cachedSize < 0) { + // getSerializedSize sets cachedSize + getSerializedSize(); + } + return cachedSize; + } /** * Computes the number of bytes required to encode this message. * The size is cached and the cached result can be retrieved * using getCachedSize(). */ - abstract public int getSerializedSize(); + public int getSerializedSize() { + // This is overridden if the generated message has serialized fields. + cachedSize = 0; + return 0; + } /** * Serializes the message and writes it to {@code output}. This does not @@ -127,7 +139,10 @@ public abstract class MessageNano { } /** - * Intended for debugging purposes only. It does not use ASCII protobuf formatting. + * Returns a string that is (mostly) compatible with ProtoBuffer's TextFormat. Note that groups + * (which are deprecated) are not serialized with the correct field name. + * + * <p>This is implemented using reflection, so it is not especially fast. */ @Override public String toString() { diff --git a/java/src/main/java/com/google/protobuf/nano/MessageNanoPrinter.java b/java/src/main/java/com/google/protobuf/nano/MessageNanoPrinter.java index 3a5ee7c..d135a51 100644 --- a/java/src/main/java/com/google/protobuf/nano/MessageNanoPrinter.java +++ b/java/src/main/java/com/google/protobuf/nano/MessageNanoPrinter.java @@ -47,20 +47,22 @@ public final class MessageNanoPrinter { private static final int MAX_STRING_LEN = 200; /** - * Returns an text representation of a MessageNano suitable for debugging. + * Returns an text representation of a MessageNano suitable for debugging. The returned string + * is mostly compatible with Protocol Buffer's TextFormat (as provided by non-nano protocol + * buffers) -- groups (which are deprecated) are output with an underscore name (e.g. foo_bar + * instead of FooBar) and will thus not parse. * * <p>Employs Java reflection on the given object and recursively prints primitive fields, * groups, and messages.</p> */ public static <T extends MessageNano> String print(T message) { if (message == null) { - return "null"; + return ""; } StringBuffer buf = new StringBuffer(); try { - print(message.getClass().getSimpleName(), message.getClass(), message, - new StringBuffer(), buf); + print(null, message.getClass(), message, new StringBuffer(), buf); } catch (IllegalAccessException e) { return "Error printing proto: " + e.getMessage(); } @@ -70,21 +72,30 @@ public final class MessageNanoPrinter { /** * Function that will print the given message/class into the StringBuffer. * Meant to be called recursively. + * + * @param identifier the identifier to use, or {@code null} if this is the root message to + * print. + * @param clazz the class of {@code message}. + * @param message the value to print. May in fact be a primitive value or byte array and not a + * message. + * @param indentBuf the indentation each line should begin with. + * @param buf the output buffer. */ private static void print(String identifier, Class<?> clazz, Object message, StringBuffer indentBuf, StringBuffer buf) throws IllegalAccessException { - if (MessageNano.class.isAssignableFrom(clazz)) { - // Nano proto message - buf.append(indentBuf).append(identifier); - - // If null, just print it and return - if (message == null) { - buf.append(": ").append(message).append("\n"); - return; + if (message == null) { + // This can happen if... + // - we're about to print a message, String, or byte[], but it not present; + // - we're about to print a primitive, but "reftype" optional style is enabled, and + // the field is unset. + // In both cases the appropriate behavior is to output nothing. + } else if (MessageNano.class.isAssignableFrom(clazz)) { // Nano proto message + int origIndentBufLength = indentBuf.length(); + if (identifier != null) { + buf.append(indentBuf).append(deCamelCaseify(identifier)).append(" <\n"); + indentBuf.append(INDENT); } - indentBuf.append(INDENT); - buf.append(" <\n"); for (Field field : clazz.getFields()) { // Proto fields are public, non-static variables that do not begin or end with '_' int modifiers = field.getModifiers(); @@ -115,15 +126,19 @@ public final class MessageNanoPrinter { print(fieldName, fieldType, value, indentBuf, buf); } } - indentBuf.delete(indentBuf.length() - INDENT.length(), indentBuf.length()); - buf.append(indentBuf).append(">\n"); + if (identifier != null) { + indentBuf.setLength(origIndentBufLength); + buf.append(indentBuf).append(">\n"); + } } else { - // Primitive value + // Non-null primitive value identifier = deCamelCaseify(identifier); buf.append(indentBuf).append(identifier).append(": "); if (message instanceof String) { String stringMessage = sanitizeString((String) message); buf.append("\"").append(stringMessage).append("\""); + } else if (message instanceof byte[]) { + appendQuotedBytes((byte[]) message, buf); } else { buf.append(message); } @@ -176,4 +191,27 @@ public final class MessageNanoPrinter { } return b.toString(); } + + /** + * Appends a quoted byte array to the provided {@code StringBuffer}. + */ + private static void appendQuotedBytes(byte[] bytes, StringBuffer builder) { + if (bytes == null) { + builder.append("\"\""); + return; + } + + builder.append('"'); + for (int i = 0; i < bytes.length; ++i) { + int ch = bytes[i]; + if (ch == '\\' || ch == '"') { + builder.append('\\').append((char) ch); + } else if (ch >= 32 && ch < 127) { + builder.append((char) ch); + } else { + builder.append(String.format("\\%03o", ch)); + } + } + builder.append('"'); + } } diff --git a/java/src/main/java/com/google/protobuf/nano/UnknownFieldData.java b/java/src/main/java/com/google/protobuf/nano/UnknownFieldData.java index 0db2a83..833ed2a 100644 --- a/java/src/main/java/com/google/protobuf/nano/UnknownFieldData.java +++ b/java/src/main/java/com/google/protobuf/nano/UnknownFieldData.java @@ -30,6 +30,8 @@ package com.google.protobuf.nano; +import java.util.Arrays; + /** * Stores unknown fields. These might be extensions or fields that the generated API doesn't * know about yet. @@ -37,6 +39,7 @@ package com.google.protobuf.nano; * @author bduff@google.com (Brian Duff) */ public final class UnknownFieldData { + final int tag; final byte[] bytes; @@ -44,4 +47,25 @@ public final class UnknownFieldData { this.tag = tag; this.bytes = bytes; } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof UnknownFieldData)) { + return false; + } + + UnknownFieldData other = (UnknownFieldData) o; + return tag == other.tag && Arrays.equals(bytes, other.bytes); + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + tag; + result = 31 * result + Arrays.hashCode(bytes); + return result; + } } diff --git a/java/src/test/java/com/google/protobuf/NanoTest.java b/java/src/test/java/com/google/protobuf/NanoTest.java index 1149c40..0e1c9bc 100644 --- a/java/src/test/java/com/google/protobuf/NanoTest.java +++ b/java/src/test/java/com/google/protobuf/NanoTest.java @@ -49,6 +49,7 @@ import com.google.protobuf.nano.NanoHasOuterClass.TestAllTypesNanoHas; import com.google.protobuf.nano.NanoOuterClass; import com.google.protobuf.nano.NanoOuterClass.TestAllTypesNano; import com.google.protobuf.nano.NanoReferenceTypes; +import com.google.protobuf.nano.NanoRepeatedPackables; import com.google.protobuf.nano.TestRepeatedMergeNano; import com.google.protobuf.nano.UnittestImportNano; import com.google.protobuf.nano.UnittestMultipleNano; @@ -60,6 +61,7 @@ import junit.framework.TestCase; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; /** @@ -2489,14 +2491,14 @@ public class NanoTest extends TestCase { msg.optionalInt32 = 14; msg.optionalFloat = 42.3f; msg.optionalString = "String \"with' both quotes"; - msg.optionalBytes = new byte[5]; + msg.optionalBytes = new byte[] {'"', '\0', 1, 8}; msg.optionalGroup = new TestAllTypesNano.OptionalGroup(); msg.optionalGroup.a = 15; msg.repeatedInt64 = new long[2]; msg.repeatedInt64[0] = 1L; msg.repeatedInt64[1] = -1L; msg.repeatedBytes = new byte[2][]; - msg.repeatedBytes[1] = new byte[5]; + msg.repeatedBytes[1] = new byte[] {'h', 'e', 'l', 'l', 'o'}; msg.repeatedGroup = new TestAllTypesNano.RepeatedGroup[2]; msg.repeatedGroup[0] = new TestAllTypesNano.RepeatedGroup(); msg.repeatedGroup[0].a = -27; @@ -2513,28 +2515,31 @@ public class NanoTest extends TestCase { msg.repeatedNestedEnum = new int[2]; msg.repeatedNestedEnum[0] = TestAllTypesNano.BAR; msg.repeatedNestedEnum[1] = TestAllTypesNano.FOO; + msg.repeatedStringPiece = new String[] {null, "world"}; String protoPrint = msg.toString(); - assertTrue(protoPrint.contains("TestAllTypesNano <")); - assertTrue(protoPrint.contains(" optional_int32: 14")); - assertTrue(protoPrint.contains(" optional_float: 42.3")); - assertTrue(protoPrint.contains(" optional_double: 0.0")); - assertTrue(protoPrint.contains(" optional_string: \"String \\u0022with\\u0027 both quotes\"")); - assertTrue(protoPrint.contains(" optional_bytes: [B@")); - assertTrue(protoPrint.contains(" optionalGroup <\n a: 15\n >")); - - assertTrue(protoPrint.contains(" repeated_int64: 1")); - assertTrue(protoPrint.contains(" repeated_int64: -1")); - assertTrue(protoPrint.contains(" repeated_bytes: null\n repeated_bytes: [B@")); - assertTrue(protoPrint.contains(" repeatedGroup <\n a: -27\n >\n" - + " repeatedGroup <\n a: -72\n >")); - assertTrue(protoPrint.contains(" optionalNestedMessage <\n bb: 7\n >")); - assertTrue(protoPrint.contains(" repeatedNestedMessage <\n bb: 77\n >\n" - + " repeatedNestedMessage <\n bb: 88\n >")); - assertTrue(protoPrint.contains(" optional_nested_enum: 3")); - assertTrue(protoPrint.contains(" repeated_nested_enum: 2\n repeated_nested_enum: 1")); - assertTrue(protoPrint.contains(" default_int32: 41")); - assertTrue(protoPrint.contains(" default_string: \"hello\"")); + assertTrue(protoPrint.contains("optional_int32: 14")); + assertTrue(protoPrint.contains("optional_float: 42.3")); + assertTrue(protoPrint.contains("optional_double: 0.0")); + assertTrue(protoPrint.contains("optional_string: \"String \\u0022with\\u0027 both quotes\"")); + assertTrue(protoPrint.contains("optional_bytes: \"\\\"\\000\\001\\010\"")); + assertTrue(protoPrint.contains("optional_group <\n a: 15\n>")); + + assertTrue(protoPrint.contains("repeated_int64: 1")); + assertTrue(protoPrint.contains("repeated_int64: -1")); + assertFalse(protoPrint.contains("repeated_bytes: \"\"")); // null should be dropped + assertTrue(protoPrint.contains("repeated_bytes: \"hello\"")); + assertTrue(protoPrint.contains("repeated_group <\n a: -27\n>\n" + + "repeated_group <\n a: -72\n>")); + assertTrue(protoPrint.contains("optional_nested_message <\n bb: 7\n>")); + assertTrue(protoPrint.contains("repeated_nested_message <\n bb: 77\n>\n" + + "repeated_nested_message <\n bb: 88\n>")); + assertTrue(protoPrint.contains("optional_nested_enum: 3")); + assertTrue(protoPrint.contains("repeated_nested_enum: 2\nrepeated_nested_enum: 1")); + assertTrue(protoPrint.contains("default_int32: 41")); + assertTrue(protoPrint.contains("default_string: \"hello\"")); + assertFalse(protoPrint.contains("repeated_string_piece: \"\"")); // null should be dropped + assertTrue(protoPrint.contains("repeated_string_piece: \"world\"")); } public void testExtensions() throws Exception { @@ -2684,6 +2689,222 @@ public class NanoTest extends TestCase { assertHasWireData(message, false); } + public void testHashCodeEquals() throws Exception { + // Complete equality: + TestAllTypesNano a = createMessageForHashCodeEqualsTest(); + TestAllTypesNano aEquivalent = createMessageForHashCodeEqualsTest(); + + // Null and empty array for repeated fields equality: + TestAllTypesNano b = createMessageForHashCodeEqualsTest(); + b.repeatedBool = null; + b.repeatedFloat = new float[0]; + TestAllTypesNano bEquivalent = createMessageForHashCodeEqualsTest(); + bEquivalent.repeatedBool = new boolean[0]; + bEquivalent.repeatedFloat = null; + + // Ref-element-type repeated fields use non-null subsequence equality: + TestAllTypesNano c = createMessageForHashCodeEqualsTest(); + c.repeatedString = null; + c.repeatedStringPiece = new String[] {null, "one", null, "two"}; + c.repeatedBytes = new byte[][] {{3, 4}, null}; + TestAllTypesNano cEquivalent = createMessageForHashCodeEqualsTest(); + cEquivalent.repeatedString = new String[3]; + cEquivalent.repeatedStringPiece = new String[] {"one", "two", null}; + cEquivalent.repeatedBytes = new byte[][] {{3, 4}}; + + // Complete equality for messages with has fields: + TestAllTypesNanoHas d = createMessageWithHasForHashCodeEqualsTest(); + TestAllTypesNanoHas dEquivalent = createMessageWithHasForHashCodeEqualsTest(); + + // If has-fields exist, fields with the same default values but + // different has-field values are different. + TestAllTypesNanoHas e = createMessageWithHasForHashCodeEqualsTest(); + e.optionalInt32++; // make different from d + e.hasDefaultString = false; + TestAllTypesNanoHas eDifferent = createMessageWithHasForHashCodeEqualsTest(); + eDifferent.optionalInt32 = e.optionalInt32; + eDifferent.hasDefaultString = true; + + // Complete equality for messages with accessors: + TestNanoAccessors f = createMessageWithAccessorsForHashCodeEqualsTest(); + TestNanoAccessors fEquivalent = createMessageWithAccessorsForHashCodeEqualsTest(); + + // If using accessors, explicitly setting a field to its default value + // should make the message different. + TestNanoAccessors g = createMessageWithAccessorsForHashCodeEqualsTest(); + g.setOptionalInt32(g.getOptionalInt32() + 1); // make different from f + g.clearDefaultString(); + TestNanoAccessors gDifferent = createMessageWithAccessorsForHashCodeEqualsTest(); + gDifferent.setOptionalInt32(g.getOptionalInt32()); + gDifferent.setDefaultString(g.getDefaultString()); + + // Complete equality for reference typed messages: + NanoReferenceTypes.TestAllTypesNano h = createRefTypedMessageForHashCodeEqualsTest(); + NanoReferenceTypes.TestAllTypesNano hEquivalent = createRefTypedMessageForHashCodeEqualsTest(); + + // Inequality of null and default value for reference typed messages: + NanoReferenceTypes.TestAllTypesNano i = createRefTypedMessageForHashCodeEqualsTest(); + i.optionalInt32 = 1; // make different from h + i.optionalFloat = null; + NanoReferenceTypes.TestAllTypesNano iDifferent = createRefTypedMessageForHashCodeEqualsTest(); + iDifferent.optionalInt32 = i.optionalInt32; + iDifferent.optionalFloat = 0.0f; + + HashMap<MessageNano, String> hashMap = new HashMap<MessageNano, String>(); + hashMap.put(a, "a"); + hashMap.put(b, "b"); + hashMap.put(c, "c"); + hashMap.put(d, "d"); + hashMap.put(e, "e"); + hashMap.put(f, "f"); + hashMap.put(g, "g"); + hashMap.put(h, "h"); + hashMap.put(i, "i"); + + assertEquals(9, hashMap.size()); // a-i should be different from each other. + + assertEquals("a", hashMap.get(a)); + assertEquals("a", hashMap.get(aEquivalent)); + + assertEquals("b", hashMap.get(b)); + assertEquals("b", hashMap.get(bEquivalent)); + + assertEquals("c", hashMap.get(c)); + assertEquals("c", hashMap.get(cEquivalent)); + + assertEquals("d", hashMap.get(d)); + assertEquals("d", hashMap.get(dEquivalent)); + + assertEquals("e", hashMap.get(e)); + assertNull(hashMap.get(eDifferent)); + + assertEquals("f", hashMap.get(f)); + assertEquals("f", hashMap.get(fEquivalent)); + + assertEquals("g", hashMap.get(g)); + assertNull(hashMap.get(gDifferent)); + + assertEquals("h", hashMap.get(h)); + assertEquals("h", hashMap.get(hEquivalent)); + + assertEquals("i", hashMap.get(i)); + assertNull(hashMap.get(iDifferent)); + } + + private TestAllTypesNano createMessageForHashCodeEqualsTest() { + TestAllTypesNano message = new TestAllTypesNano(); + message.optionalInt32 = 5; + message.optionalInt64 = 777; + message.optionalFloat = 1.0f; + message.optionalDouble = 2.0; + message.optionalBool = true; + message.optionalString = "Hello"; + message.optionalBytes = new byte[] { 1, 2, 3 }; + message.optionalNestedMessage = new TestAllTypesNano.NestedMessage(); + message.optionalNestedMessage.bb = 27; + message.optionalNestedEnum = TestAllTypesNano.BAR; + message.repeatedInt32 = new int[] { 5, 6, 7, 8 }; + message.repeatedInt64 = new long[] { 27L, 28L, 29L }; + message.repeatedFloat = new float[] { 5.0f, 6.0f }; + message.repeatedDouble = new double[] { 99.1, 22.5 }; + message.repeatedBool = new boolean[] { true, false, true }; + message.repeatedString = new String[] { "One", "Two" }; + message.repeatedBytes = new byte[][] { { 2, 7 }, { 2, 7 } }; + message.repeatedNestedMessage = new TestAllTypesNano.NestedMessage[] { + message.optionalNestedMessage, + message.optionalNestedMessage + }; + message.repeatedNestedEnum = new int[] { + 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; + } + + private TestAllTypesNanoHas createMessageWithHasForHashCodeEqualsTest() { + TestAllTypesNanoHas message = new TestAllTypesNanoHas(); + message.optionalInt32 = 5; + message.optionalString = "Hello"; + message.optionalBytes = new byte[] { 1, 2, 3 }; + message.optionalNestedMessage = new TestAllTypesNanoHas.NestedMessage(); + message.optionalNestedMessage.bb = 27; + message.optionalNestedEnum = TestAllTypesNano.BAR; + message.repeatedInt32 = new int[] { 5, 6, 7, 8 }; + message.repeatedString = new String[] { "One", "Two" }; + message.repeatedBytes = new byte[][] { { 2, 7 }, { 2, 7 } }; + message.repeatedNestedMessage = new TestAllTypesNanoHas.NestedMessage[] { + message.optionalNestedMessage, + message.optionalNestedMessage + }; + message.repeatedNestedEnum = new int[] { + TestAllTypesNano.BAR, + TestAllTypesNano.BAZ + }; + message.defaultFloatNan = 1.0f; + return message; + } + + private TestNanoAccessors createMessageWithAccessorsForHashCodeEqualsTest() { + TestNanoAccessors message = new TestNanoAccessors() + .setOptionalInt32(5) + .setOptionalString("Hello") + .setOptionalBytes(new byte[] {1, 2, 3}) + .setOptionalNestedMessage(new TestNanoAccessors.NestedMessage().setBb(27)) + .setOptionalNestedEnum(TestNanoAccessors.BAR) + .setDefaultFloatNan(1.0f); + message.repeatedInt32 = new int[] { 5, 6, 7, 8 }; + message.repeatedString = new String[] { "One", "Two" }; + message.repeatedBytes = new byte[][] { { 2, 7 }, { 2, 7 } }; + message.repeatedNestedMessage = new TestNanoAccessors.NestedMessage[] { + message.getOptionalNestedMessage(), + message.getOptionalNestedMessage() + }; + message.repeatedNestedEnum = new int[] { + TestAllTypesNano.BAR, + TestAllTypesNano.BAZ + }; + return message; + } + + private NanoReferenceTypes.TestAllTypesNano createRefTypedMessageForHashCodeEqualsTest() { + NanoReferenceTypes.TestAllTypesNano message = new NanoReferenceTypes.TestAllTypesNano(); + message.optionalInt32 = 5; + message.optionalInt64 = 777L; + message.optionalFloat = 1.0f; + message.optionalDouble = 2.0; + message.optionalBool = true; + message.optionalString = "Hello"; + message.optionalBytes = new byte[] { 1, 2, 3 }; + message.optionalNestedMessage = + new NanoReferenceTypes.TestAllTypesNano.NestedMessage(); + message.optionalNestedMessage.foo = 27; + message.optionalNestedEnum = NanoReferenceTypes.TestAllTypesNano.BAR; + message.repeatedInt32 = new int[] { 5, 6, 7, 8 }; + message.repeatedInt64 = new long[] { 27L, 28L, 29L }; + message.repeatedFloat = new float[] { 5.0f, 6.0f }; + message.repeatedDouble = new double[] { 99.1, 22.5 }; + message.repeatedBool = new boolean[] { true, false, true }; + message.repeatedString = new String[] { "One", "Two" }; + message.repeatedBytes = new byte[][] { { 2, 7 }, { 2, 7 } }; + message.repeatedNestedMessage = + new NanoReferenceTypes.TestAllTypesNano.NestedMessage[] { + message.optionalNestedMessage, + message.optionalNestedMessage + }; + message.repeatedNestedEnum = new int[] { + NanoReferenceTypes.TestAllTypesNano.BAR, + NanoReferenceTypes.TestAllTypesNano.BAZ + }; + return message; + } + public void testNullRepeatedFields() throws Exception { // Check that serialization after explicitly setting a repeated field // to null doesn't NPE. @@ -2746,6 +2967,37 @@ public class NanoTest extends TestCase { assertEquals(TestAllTypesNano.BAR, message.repeatedPackedNestedEnum[1]); } + public void testNullRepeatedFieldElements() throws Exception { + // Check that serialization with null array elements doesn't NPE. + String string1 = "1"; + String string2 = "2"; + byte[] bytes1 = {3, 4}; + byte[] bytes2 = {5, 6}; + TestAllTypesNano.NestedMessage msg1 = new TestAllTypesNano.NestedMessage(); + msg1.bb = 7; + TestAllTypesNano.NestedMessage msg2 = new TestAllTypesNano.NestedMessage(); + msg2.bb = 8; + + TestAllTypesNano message = new TestAllTypesNano(); + message.repeatedString = new String[] {null, string1, string2}; + message.repeatedBytes = new byte[][] {bytes1, null, bytes2}; + message.repeatedNestedMessage = new TestAllTypesNano.NestedMessage[] {msg1, msg2, null}; + message.repeatedGroup = new TestAllTypesNano.RepeatedGroup[] {null, null, null}; + + byte[] serialized = MessageNano.toByteArray(message); // should not NPE + TestAllTypesNano deserialized = MessageNano.mergeFrom(new TestAllTypesNano(), serialized); + assertEquals(2, deserialized.repeatedString.length); + assertEquals(string1, deserialized.repeatedString[0]); + assertEquals(string2, deserialized.repeatedString[1]); + assertEquals(2, deserialized.repeatedBytes.length); + assertTrue(Arrays.equals(bytes1, deserialized.repeatedBytes[0])); + assertTrue(Arrays.equals(bytes2, deserialized.repeatedBytes[1])); + assertEquals(2, deserialized.repeatedNestedMessage.length); + assertEquals(msg1.bb, deserialized.repeatedNestedMessage[0].bb); + assertEquals(msg2.bb, deserialized.repeatedNestedMessage[1].bb); + assertEquals(0, deserialized.repeatedGroup.length); + } + public void testRepeatedMerge() throws Exception { // Check that merging repeated fields cause the arrays to expand with // new data. @@ -2813,6 +3065,77 @@ public class NanoTest extends TestCase { assertEquals(30, firstContainer.contained.repeatedInt32[2]); } + public void testRepeatedPackables() throws Exception { + // 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}; + nonPacked.fixed32S = new int[] {19, 20, 21}; + nonPacked.fixed64S = new long[] {22, 23, 24}; + nonPacked.sfixed32S = new int[] {25, 26, 27}; + nonPacked.sfixed64S = new long[] {28, 29, 30}; + nonPacked.floats = new float[] {31, 32, 33}; + nonPacked.doubles = new double[] {34, 35, 36}; + nonPacked.bools = new boolean[] {false, true}; + nonPacked.enums = new int[] { + NanoRepeatedPackables.Enum.OPTION_ONE, + NanoRepeatedPackables.Enum.OPTION_TWO, + }; + nonPacked.noise = 13579; + + byte[] nonPackedSerialized = MessageNano.toByteArray(nonPacked); + + NanoRepeatedPackables.Packed packed = + MessageNano.mergeFrom(new NanoRepeatedPackables.Packed(), nonPackedSerialized); + assertRepeatedPackablesEqual(nonPacked, packed); + + byte[] packedSerialized = MessageNano.toByteArray(packed); + // Just a cautious check that the two serialized forms are different, + // to make sure the remaining of this test is useful: + assertFalse(Arrays.equals(nonPackedSerialized, packedSerialized)); + + nonPacked = MessageNano.mergeFrom(new NanoRepeatedPackables.NonPacked(), packedSerialized); + assertRepeatedPackablesEqual(nonPacked, packed); + + // Test mixed serialized form. + byte[] mixedSerialized = new byte[nonPackedSerialized.length + packedSerialized.length]; + System.arraycopy(nonPackedSerialized, 0, mixedSerialized, 0, nonPackedSerialized.length); + System.arraycopy(packedSerialized, 0, + mixedSerialized, nonPackedSerialized.length, packedSerialized.length); + + 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[] {25, 26, 27, 25, 26, 27}, nonPacked.sfixed32S)); + assertTrue(Arrays.equals(new boolean[] {false, true, false, true}, nonPacked.bools)); + } + + private void assertRepeatedPackablesEqual( + NanoRepeatedPackables.NonPacked nonPacked, NanoRepeatedPackables.Packed packed) { + // Not using MessageNano.equals() -- that belongs to a separate test. + assertTrue(Arrays.equals(nonPacked.int32S, packed.int32S)); + assertTrue(Arrays.equals(nonPacked.int64S, packed.int64S)); + assertTrue(Arrays.equals(nonPacked.uint32S, packed.uint32S)); + assertTrue(Arrays.equals(nonPacked.uint64S, packed.uint64S)); + assertTrue(Arrays.equals(nonPacked.sint32S, packed.sint32S)); + assertTrue(Arrays.equals(nonPacked.sint64S, packed.sint64S)); + assertTrue(Arrays.equals(nonPacked.fixed32S, packed.fixed32S)); + assertTrue(Arrays.equals(nonPacked.fixed64S, packed.fixed64S)); + assertTrue(Arrays.equals(nonPacked.sfixed32S, packed.sfixed32S)); + assertTrue(Arrays.equals(nonPacked.sfixed64S, packed.sfixed64S)); + assertTrue(Arrays.equals(nonPacked.floats, packed.floats)); + assertTrue(Arrays.equals(nonPacked.doubles, packed.doubles)); + assertTrue(Arrays.equals(nonPacked.bools, packed.bools)); + assertTrue(Arrays.equals(nonPacked.enums, packed.enums)); + } + private void assertHasWireData(MessageNano message, boolean expected) { byte[] bytes = MessageNano.toByteArray(message); int wireLength = bytes.length; diff --git a/src/google/protobuf/compiler/javanano/javanano_enum_field.cc b/src/google/protobuf/compiler/javanano/javanano_enum_field.cc index 3c4b978..17f0e26 100644 --- a/src/google/protobuf/compiler/javanano/javanano_enum_field.cc +++ b/src/google/protobuf/compiler/javanano/javanano_enum_field.cc @@ -159,8 +159,46 @@ GenerateSerializedSizeCode(io::Printer* printer) const { } } -string EnumFieldGenerator::GetBoxedType() const { - return ClassName(params_, descriptor_->enum_type()); +void EnumFieldGenerator::GenerateEqualsCode(io::Printer* printer) const { + if (params_.use_reference_types_for_primitives()) { + printer->Print(variables_, + "if (this.$name$ == null) {\n" + " if (other.$name$ != null) {\n" + " return false;\n" + " }\n" + "} else if (!this.$name$.equals(other.$name$)) {\n" + " return false;" + "}\n"); + } else { + // We define equality as serialized form equality. If generate_has(), + // then if the field value equals the default value in both messages, + // but one's 'has' field is set and the other's is not, the serialized + // forms are different and we should return false. + printer->Print(variables_, + "if (this.$name$ != other.$name$"); + if (params_.generate_has()) { + printer->Print(variables_, + "\n" + " || (this.$name$ == $default$\n" + " && this.has$capitalized_name$ != other.has$capitalized_name$)"); + } + printer->Print(") {\n" + " return false;\n" + "}\n"); + } +} + +void EnumFieldGenerator::GenerateHashCodeCode(io::Printer* printer) const { + printer->Print( + "result = 31 * result + "); + if (params_.use_reference_types_for_primitives()) { + printer->Print(variables_, + "(this.$name$ == null ? 0 : this.$name$)"); + } else { + printer->Print(variables_, + "this.$name$"); + } + printer->Print(";\n"); } // =================================================================== @@ -227,8 +265,19 @@ GenerateSerializedSizeCode(io::Printer* printer) const { "}\n"); } -string AccessorEnumFieldGenerator::GetBoxedType() const { - return ClassName(params_, descriptor_->enum_type()); +void AccessorEnumFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($different_has$\n" + " || $name$_ != other.$name$_) {\n" + " return false;\n" + "}\n"); +} + +void AccessorEnumFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + printer->Print(variables_, + "result = 31 * result + $name$_;\n"); } // =================================================================== @@ -245,10 +294,6 @@ void RepeatedEnumFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "public $type$[] $name$;\n"); - if (descriptor_->options().packed()) { - printer->Print(variables_, - "private int $name$MemoizedSerializedSize;\n"); - } } void RepeatedEnumFieldGenerator:: @@ -260,45 +305,58 @@ GenerateClearCode(io::Printer* printer) const { void RepeatedEnumFieldGenerator:: GenerateMergingCode(io::Printer* printer) const { // First, figure out the length of the array, then parse. - if (descriptor_->options().packed()) { - printer->Print(variables_, - "int length = input.readRawVarint32();\n" - "int limit = input.pushLimit(length);\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" - "}\n" - "for (; i < newArray.length; i++) {\n" - " newArray[i] = input.readInt32();\n" - "}\n" - "this.$name$ = newArray;\n" - "input.popLimit(limit);\n"); - } else { - 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" - "}\n" - "// Last one without readTag.\n" - "newArray[i] = input.readInt32();\n" - "this.$name$ = newArray;\n"); - } + 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" + "}\n" + "// Last one without readTag.\n" + "newArray[i] = input.readInt32();\n" + "this.$name$ = newArray;\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateMergingCodeFromPacked(io::Printer* printer) const { + printer->Print(variables_, + "int length = input.readRawVarint32();\n" + "int limit = input.pushLimit(length);\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" + "}\n" + "for (; i < newArray.length; i++) {\n" + " newArray[i] = input.readInt32();\n" + "}\n" + "this.$name$ = newArray;\n" + "input.popLimit(limit);\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateRepeatedDataSizeCode(io::Printer* printer) const { + // Creates a variable dataSize and puts the serialized size in there. + printer->Print(variables_, + "int dataSize = 0;\n" + "for (int i = 0; i < this.$name$.length; i++) {\n" + " int element = this.$name$[i];\n" + " dataSize += com.google.protobuf.nano.CodedOutputByteBufferNano\n" + " .computeInt32SizeNoTag(element);\n" + "}\n"); } void RepeatedEnumFieldGenerator:: @@ -308,18 +366,20 @@ GenerateSerializationCode(io::Printer* printer) const { printer->Indent(); if (descriptor_->options().packed()) { + GenerateRepeatedDataSizeCode(printer); printer->Print(variables_, "output.writeRawVarint32($tag$);\n" - "output.writeRawVarint32($name$MemoizedSerializedSize);\n" - "for (int element : this.$name$) {\n" - " output.writeRawVarint32(element);\n" + "output.writeRawVarint32(dataSize);\n" + "for (int i = 0; i < this.$name$.length; i++) {\n" + " output.writeRawVarint32(this.$name$[i]);\n" "}\n"); } else { printer->Print(variables_, - "for (int element : this.$name$) {\n" - " output.writeInt32($number$, element);\n" + "for (int i = 0; i < this.$name$.length; i++) {\n" + " output.writeInt32($number$, this.$name$[i]);\n" "}\n"); } + printer->Outdent(); printer->Print(variables_, "}\n"); @@ -331,43 +391,40 @@ GenerateSerializedSizeCode(io::Printer* printer) const { "if (this.$name$ != null && this.$name$.length > 0) {\n"); printer->Indent(); - printer->Print(variables_, - "int dataSize = 0;\n" - "for (int element : this.$name$) {\n" - " dataSize += com.google.protobuf.nano.CodedOutputByteBufferNano\n" - " .computeInt32SizeNoTag(element);\n" - "}\n"); + GenerateRepeatedDataSizeCode(printer); printer->Print( "size += dataSize;\n"); if (descriptor_->options().packed()) { - // cache the data size for packed fields. printer->Print(variables_, "size += $tag_size$;\n" "size += com.google.protobuf.nano.CodedOutputByteBufferNano\n" - " .computeRawVarint32Size(dataSize);\n" - "$name$MemoizedSerializedSize = dataSize;\n"); + " .computeRawVarint32Size(dataSize);\n"); } else { printer->Print(variables_, - "size += $tag_size$ * this.$name$.length;\n"); + "size += $tag_size$ * this.$name$.length;\n"); } printer->Outdent(); - // set cached size to 0 for empty packed fields. - if (descriptor_->options().packed()) { - printer->Print(variables_, - "} else {\n" - " $name$MemoizedSerializedSize = 0;\n" - "}\n"); - } else { - printer->Print( - "}\n"); - } + printer->Print( + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!com.google.protobuf.nano.InternalNano.equals(\n" + " this.$name$, other.$name$)) {\n" + " return false;\n" + "}\n"); } -string RepeatedEnumFieldGenerator::GetBoxedType() const { - return ClassName(params_, descriptor_->enum_type()); +void RepeatedEnumFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + printer->Print(variables_, + "result = 31 * result\n" + " + com.google.protobuf.nano.InternalNano.hashCode(this.$name$);\n"); } } // namespace javanano diff --git a/src/google/protobuf/compiler/javanano/javanano_enum_field.h b/src/google/protobuf/compiler/javanano/javanano_enum_field.h index 934aaaa..9000d20 100644 --- a/src/google/protobuf/compiler/javanano/javanano_enum_field.h +++ b/src/google/protobuf/compiler/javanano/javanano_enum_field.h @@ -55,8 +55,8 @@ class EnumFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; - - string GetBoxedType() const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; private: const FieldDescriptor* descriptor_; @@ -77,8 +77,8 @@ class AccessorEnumFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; - - string GetBoxedType() const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; private: const FieldDescriptor* descriptor_; @@ -96,12 +96,15 @@ class RepeatedEnumFieldGenerator : public FieldGenerator { void GenerateMembers(io::Printer* printer) const; void GenerateClearCode(io::Printer* printer) const; void GenerateMergingCode(io::Printer* printer) const; + void GenerateMergingCodeFromPacked(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; - - string GetBoxedType() const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; private: + void GenerateRepeatedDataSizeCode(io::Printer* printer) const; + const FieldDescriptor* descriptor_; map<string, string> variables_; diff --git a/src/google/protobuf/compiler/javanano/javanano_field.cc b/src/google/protobuf/compiler/javanano/javanano_field.cc index 6629f96..62e133e 100644 --- a/src/google/protobuf/compiler/javanano/javanano_field.cc +++ b/src/google/protobuf/compiler/javanano/javanano_field.cc @@ -46,6 +46,16 @@ namespace javanano { FieldGenerator::~FieldGenerator() {} +void FieldGenerator::GenerateMergingCodeFromPacked(io::Printer* printer) const { + // Reaching here indicates a bug. Cases are: + // - This FieldGenerator should support packing, but this method should be + // overridden. + // - This FieldGenerator doesn't support packing, and this method should + // never have been called. + GOOGLE_LOG(FATAL) << "GenerateParsingCodeFromPacked() " + << "called on field generator that does not support packing."; +} + FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor, const Params ¶ms) : descriptor_(descriptor), field_generators_( diff --git a/src/google/protobuf/compiler/javanano/javanano_field.h b/src/google/protobuf/compiler/javanano/javanano_field.h index dad2e7a..14b6489 100644 --- a/src/google/protobuf/compiler/javanano/javanano_field.h +++ b/src/google/protobuf/compiler/javanano/javanano_field.h @@ -60,10 +60,16 @@ class FieldGenerator { virtual void GenerateMembers(io::Printer* printer) const = 0; virtual void GenerateClearCode(io::Printer* printer) const = 0; virtual void GenerateMergingCode(io::Printer* printer) const = 0; + + // Generates code to merge from packed serialized form. The default + // implementation will fail; subclasses which can handle packed serialized + // forms will override this and print appropriate code to the printer. + virtual void GenerateMergingCodeFromPacked(io::Printer* printer) const; + virtual void GenerateSerializationCode(io::Printer* printer) const = 0; virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0; - - virtual string GetBoxedType() const = 0; + virtual void GenerateEqualsCode(io::Printer* printer) const = 0; + virtual void GenerateHashCodeCode(io::Printer* printer) const = 0; protected: const Params& params_; diff --git a/src/google/protobuf/compiler/javanano/javanano_file.cc b/src/google/protobuf/compiler/javanano/javanano_file.cc index 4e9fe0a..bdc51c4 100644 --- a/src/google/protobuf/compiler/javanano/javanano_file.cc +++ b/src/google/protobuf/compiler/javanano/javanano_file.cc @@ -177,9 +177,7 @@ void FileGenerator::Generate(io::Printer* printer) { printer->Print( "\n" "@SuppressWarnings(\"hiding\")\n" - "public final class $classname$ {\n" - " \n" - " private $classname$() {}\n", + "public interface $classname$ {\n", "classname", classname_); printer->Indent(); diff --git a/src/google/protobuf/compiler/javanano/javanano_generator.cc b/src/google/protobuf/compiler/javanano/javanano_generator.cc index 48c3a21..5c8bcb6 100644 --- a/src/google/protobuf/compiler/javanano/javanano_generator.cc +++ b/src/google/protobuf/compiler/javanano/javanano_generator.cc @@ -125,6 +125,8 @@ bool JavaNanoGenerator::Generate(const FileDescriptor* file, } 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 { *error = "Ignore unknown javanano generator option: " + options[i].first; } diff --git a/src/google/protobuf/compiler/javanano/javanano_helpers.cc b/src/google/protobuf/compiler/javanano/javanano_helpers.cc index ed6dbd7..95ee670 100644 --- a/src/google/protobuf/compiler/javanano/javanano_helpers.cc +++ b/src/google/protobuf/compiler/javanano/javanano_helpers.cc @@ -482,7 +482,7 @@ string GenerateGetBit(int bit_index) { int bit_in_var_index = bit_index % 32; string mask = kBitMasks[bit_in_var_index]; - string result = "((" + var_name + " & " + mask + ") == " + mask + ")"; + string result = "((" + var_name + " & " + mask + ") != 0)"; return result; } @@ -504,11 +504,22 @@ string GenerateClearBit(int bit_index) { return result; } +string GenerateDifferentBit(int bit_index) { + string var_name = GetBitFieldNameForBit(bit_index); + int bit_in_var_index = bit_index % 32; + + string mask = kBitMasks[bit_in_var_index]; + string result = "((" + var_name + " & " + mask + + ") != (other." + var_name + " & " + mask + "))"; + return result; +} + void SetBitOperationVariables(const string name, int bitIndex, map<string, string>* variables) { (*variables)["get_" + name] = GenerateGetBit(bitIndex); (*variables)["set_" + name] = GenerateSetBit(bitIndex); (*variables)["clear_" + name] = GenerateClearBit(bitIndex); + (*variables)["different_" + name] = GenerateDifferentBit(bitIndex); } } // namespace javanano diff --git a/src/google/protobuf/compiler/javanano/javanano_helpers.h b/src/google/protobuf/compiler/javanano/javanano_helpers.h index cddc077..753a4bd 100644 --- a/src/google/protobuf/compiler/javanano/javanano_helpers.h +++ b/src/google/protobuf/compiler/javanano/javanano_helpers.h @@ -141,30 +141,37 @@ string DefaultValue(const Params& params, const FieldDescriptor* field); // Methods for shared bitfields. -// Gets the name of the shared bitfield for the given index. +// Gets the name of the shared bitfield for the given field index. string GetBitFieldName(int index); // Gets the name of the shared bitfield for the given bit index. // Effectively, GetBitFieldName(bit_index / 32) string GetBitFieldNameForBit(int bit_index); -// Generates the java code for the expression that returns the boolean value -// of the bit of the shared bitfields for the given bit index. -// Example: "((bitField1_ & 0x04) == 0x04)" +// Generates the java code for the expression that returns whether the bit at +// the given bit index is set. +// Example: "((bitField1_ & 0x04000000) != 0)" string GenerateGetBit(int bit_index); -// Generates the java code for the expression that sets the bit of the shared -// bitfields for the given bit index. -// Example: "bitField1_ = (bitField1_ | 0x04)" +// Generates the java code for the expression that sets the bit at the given +// bit index. +// Example: "bitField1_ |= 0x04000000" string GenerateSetBit(int bit_index); -// Generates the java code for the expression that clears the bit of the shared -// bitfields for the given bit index. -// Example: "bitField1_ = (bitField1_ & ~0x04)" +// Generates the java code for the expression that clears the bit at the given +// bit index. +// Example: "bitField1_ = (bitField1_ & ~0x04000000)" string GenerateClearBit(int bit_index); -// Sets the 'get_*', 'set_*' and 'clear_*' variables, where * is the given bit -// field name, to the appropriate Java expressions for the given bit index. +// Generates the java code for the expression that returns whether the bit at +// the given bit index contains different values in the current object and +// another object accessible via the variable 'other'. +// Example: "((bitField1_ & 0x04000000) != (other.bitField1_ & 0x04000000))" +string GenerateDifferentBit(int bit_index); + +// Sets the 'get_*', 'set_*', 'clear_*' and 'different_*' variables, where * is +// the given name of the bit, to the appropriate Java expressions for the given +// bit index. void SetBitOperationVariables(const string name, int bitIndex, map<string, string>* variables); diff --git a/src/google/protobuf/compiler/javanano/javanano_message.cc b/src/google/protobuf/compiler/javanano/javanano_message.cc index c55ca55..f7ab62c 100644 --- a/src/google/protobuf/compiler/javanano/javanano_message.cc +++ b/src/google/protobuf/compiler/javanano/javanano_message.cc @@ -139,16 +139,21 @@ void MessageGenerator::Generate(io::Printer* printer) { printer->Print( "\n" "@SuppressWarnings(\"hiding\")\n" - "public final class $classname$ extends\n" - " com.google.protobuf.nano.MessageNano {\n", + "public final class $classname$ extends\n", "classname", descriptor_->name()); } else { printer->Print( "\n" - "public static final class $classname$ extends\n" - " com.google.protobuf.nano.MessageNano {\n", + "public static final class $classname$ extends\n", "classname", descriptor_->name()); } + if (params_.store_unknown_fields()) { + printer->Print( + " com.google.protobuf.nano.ExtendableMessageNano {\n"); + } else { + printer->Print( + " com.google.protobuf.nano.MessageNano {\n"); + } printer->Indent(); printer->Print( "\n" @@ -159,13 +164,6 @@ void MessageGenerator::Generate(io::Printer* printer) { "}\n", "classname", descriptor_->name()); - if (params_.store_unknown_fields()) { - printer->Print( - "\n" - "private java.util.List<com.google.protobuf.nano.UnknownFieldData>\n" - " unknownFieldData;\n"); - } - // Nested types and extensions for (int i = 0; i < descriptor_->extension_count(); i++) { ExtensionGenerator(descriptor_->extension(i), params_).Generate(printer); @@ -198,25 +196,11 @@ void MessageGenerator::Generate(io::Printer* printer) { GenerateClear(printer); - // If we have an extension range, generate accessors for extensions. - if (params_.store_unknown_fields() - && descriptor_->extension_range_count() > 0) { - printer->Print( - "\n" - "public <T> T getExtension(com.google.protobuf.nano.Extension<T> extension) {\n" - " return com.google.protobuf.nano.WireFormatNano.getExtension(\n" - " extension, unknownFieldData);\n" - "}\n" - "\n" - "public <T> void setExtension(com.google.protobuf.nano.Extension<T> extension, T value) {\n" - " if (unknownFieldData == null) {\n" - " unknownFieldData =\n" - " new java.util.ArrayList<com.google.protobuf.nano.UnknownFieldData>();\n" - " }\n" - " com.google.protobuf.nano.WireFormatNano.setExtension(\n" - " extension, value, unknownFieldData);\n" - "}\n"); + if (params_.generate_equals()) { + GenerateEquals(printer); + GenerateHashCode(printer); } + GenerateMessageSerializationMethods(printer); GenerateMergeFromMethods(printer); GenerateParseFromMethods(printer); @@ -260,38 +244,28 @@ GenerateMessageSerializationMethods(io::Printer* printer) { } printer->Outdent(); - printer->Print( - "}\n" - "\n" - "private int cachedSize;\n" - "@Override\n" - "public int getCachedSize() {\n" - " if (cachedSize < 0) {\n" - " // getSerializedSize sets cachedSize\n" - " getSerializedSize();\n" - " }\n" - " return cachedSize;\n" - "}\n" - "\n" - "@Override\n" - "public int getSerializedSize() {\n" - " int size = 0;\n"); - printer->Indent(); + printer->Print("}\n"); - for (int i = 0; i < descriptor_->field_count(); i++) { - field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer); - } + // Rely on the parent implementation of getSerializedSize if there are no fields to + // serialize in this MessageNano. + if (descriptor_->field_count() != 0) { + printer->Print( + "\n" + "@Override\n" + "public int getSerializedSize() {\n" + " int size = super.getSerializedSize();\n"); + printer->Indent(); - if (params_.store_unknown_fields()) { + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer); + } + + printer->Outdent(); printer->Print( - "size += com.google.protobuf.nano.WireFormatNano.computeWireSize(unknownFieldData);\n"); + " cachedSize = size;\n" + " return size;\n" + "}\n"); } - - printer->Outdent(); - printer->Print( - " cachedSize = size;\n" - " return size;\n" - "}\n"); } void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) { @@ -326,11 +300,11 @@ void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) { if (params_.store_unknown_fields()) { printer->Print( "if (unknownFieldData == null) {\n" - " unknownFieldData = \n" + " unknownFieldData =\n" " new java.util.ArrayList<com.google.protobuf.nano.UnknownFieldData>();\n" "}\n" - "if (!com.google.protobuf.nano.WireFormatNano.storeUnknownField(unknownFieldData, \n" - " input, tag)) {\n" + "if (!com.google.protobuf.nano.WireFormatNano.storeUnknownField(\n" + " unknownFieldData, input, tag)) {\n" " return this;\n" "}\n"); } else { @@ -346,7 +320,7 @@ void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) { for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = sorted_fields[i]; uint32 tag = WireFormatLite::MakeTag(field->number(), - WireFormat::WireTypeForField(field)); + WireFormat::WireTypeForFieldType(field->type())); printer->Print( "case $tag$: {\n", @@ -359,6 +333,24 @@ void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) { printer->Print( " break;\n" "}\n"); + + if (field->is_packable()) { + // To make packed = true wire compatible, we generate parsing code from a + // packed version of this field regardless of field->options().packed(). + uint32 packed_tag = WireFormatLite::MakeTag(field->number(), + WireFormatLite::WIRETYPE_LENGTH_DELIMITED); + printer->Print( + "case $tag$: {\n", + "tag", SimpleItoa(packed_tag)); + printer->Indent(); + + field_generators_.get(field).GenerateMergingCodeFromPacked(printer); + + printer->Outdent(); + printer->Print( + " break;\n" + "}\n"); + } } printer->Outdent(); @@ -427,6 +419,79 @@ void MessageGenerator::GenerateClear(io::Printer* printer) { "}\n"); } +void MessageGenerator::GenerateEquals(io::Printer* printer) { + // Don't override if there are no fields. We could generate an + // equals method that compares types, but often empty messages + // are used as namespaces. + if (descriptor_->field_count() == 0 && !params_.store_unknown_fields()) { + return; + } + + printer->Print( + "\n" + "@Override\n" + "public boolean equals(Object o) {\n"); + printer->Indent(); + printer->Print( + "if (o == this) {\n" + " return true;\n" + "}\n" + "if (!(o instanceof $classname$)) {\n" + " return false;\n" + "}\n" + "$classname$ other = ($classname$) o;\n", + "classname", descriptor_->name()); + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + field_generators_.get(field).GenerateEqualsCode(printer); + } + + if (params_.store_unknown_fields()) { + printer->Print( + "if (unknownFieldData == null || unknownFieldData.isEmpty()) {\n" + " return other.unknownFieldData == null || other.unknownFieldData.isEmpty();" + "} else {\n" + " return unknownFieldData.equals(other.unknownFieldData);\n" + "}\n"); + } else { + printer->Print( + "return true;\n"); + } + + printer->Outdent(); + printer->Print("}\n"); +} + +void MessageGenerator::GenerateHashCode(io::Printer* printer) { + if (descriptor_->field_count() == 0 && !params_.store_unknown_fields()) { + return; + } + + printer->Print( + "\n" + "@Override\n" + "public int hashCode() {\n"); + printer->Indent(); + + printer->Print("int result = 17;\n"); + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + field_generators_.get(field).GenerateHashCodeCode(printer); + } + + if (params_.store_unknown_fields()) { + printer->Print( + "result = 31 * result + (unknownFieldData == null || unknownFieldData.isEmpty()\n" + " ? 0 : unknownFieldData.hashCode());\n"); + } + + printer->Print("return result;\n"); + + printer->Outdent(); + printer->Print("}\n"); +} + // =================================================================== } // namespace javanano diff --git a/src/google/protobuf/compiler/javanano/javanano_message.h b/src/google/protobuf/compiler/javanano/javanano_message.h index d59ec9f..f87f84f 100644 --- a/src/google/protobuf/compiler/javanano/javanano_message.h +++ b/src/google/protobuf/compiler/javanano/javanano_message.h @@ -36,9 +36,10 @@ #define GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_H__ #include <string> -#include <google/protobuf/stubs/common.h> -#include <google/protobuf/compiler/javanano/javanano_params.h> +#include <google/protobuf/compiler/javanano/javanano_helpers.h> #include <google/protobuf/compiler/javanano/javanano_field.h> +#include <google/protobuf/compiler/javanano/javanano_params.h> +#include <google/protobuf/stubs/common.h> namespace google { namespace protobuf { @@ -76,6 +77,8 @@ class MessageGenerator { const FieldDescriptor* field); void GenerateClear(io::Printer* printer); + void GenerateEquals(io::Printer* printer); + void GenerateHashCode(io::Printer* printer); const Params& params_; const Descriptor* descriptor_; diff --git a/src/google/protobuf/compiler/javanano/javanano_message_field.cc b/src/google/protobuf/compiler/javanano/javanano_message_field.cc index 02253f9..d9abea3 100644 --- a/src/google/protobuf/compiler/javanano/javanano_message_field.cc +++ b/src/google/protobuf/compiler/javanano/javanano_message_field.cc @@ -126,8 +126,25 @@ GenerateSerializedSizeCode(io::Printer* printer) const { "}\n"); } -string MessageFieldGenerator::GetBoxedType() const { - return ClassName(params_, descriptor_->message_type()); +void MessageFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "if (this.$name$ == null) { \n" + " if (other.$name$ != null) {\n" + " return false;\n" + " }\n" + "} else {\n" + " if (!this.$name$.equals(other.$name$)) {\n" + " return false;\n" + " }\n" + "}\n"); +} + +void MessageFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + printer->Print(variables_, + "result = 31 * result +\n" + " (this.$name$ == null ? 0 : this.$name$.hashCode());\n"); } // =================================================================== @@ -203,8 +220,22 @@ GenerateSerializedSizeCode(io::Printer* printer) const { "}\n"); } -string AccessorMessageFieldGenerator::GetBoxedType() const { - return ClassName(params_, descriptor_->message_type()); +void AccessorMessageFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($name$_ == null) {\n" + " if (other.$name$_ != null) {\n" + " return false;\n" + " }\n" + "} else if (!$name$_.equals(other.$name$_)) {\n" + " return false;\n" + "}\n"); +} + +void AccessorMessageFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + printer->Print(variables_, + "result = 31 * result + ($name$_ == null ? 0 : $name$_.hashCode());\n"); } // =================================================================== @@ -273,9 +304,12 @@ GenerateMergingCode(io::Printer* printer) const { void RepeatedMessageFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { printer->Print(variables_, - "if (this.$name$ != null) {\n" - " for ($type$ element : this.$name$) {\n" - " output.write$group_or_message$($number$, element);\n" + "if (this.$name$ != null && this.$name$.length > 0) {\n" + " for (int i = 0; i < this.$name$.length; i++) {\n" + " $type$ element = this.$name$[i];\n" + " if (element != null) {\n" + " output.write$group_or_message$($number$, element);\n" + " }\n" " }\n" "}\n"); } @@ -283,16 +317,31 @@ GenerateSerializationCode(io::Printer* printer) const { void RepeatedMessageFieldGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, - "if (this.$name$ != null) {\n" - " for ($type$ element : this.$name$) {\n" - " size += com.google.protobuf.nano.CodedOutputByteBufferNano\n" - " .compute$group_or_message$Size($number$, element);\n" + "if (this.$name$ != null && this.$name$.length > 0) {\n" + " for (int i = 0; i < this.$name$.length; i++) {\n" + " $type$ element = this.$name$[i];\n" + " if (element != null) {\n" + " size += com.google.protobuf.nano.CodedOutputByteBufferNano\n" + " .compute$group_or_message$Size($number$, element);\n" + " }\n" " }\n" "}\n"); } -string RepeatedMessageFieldGenerator::GetBoxedType() const { - return ClassName(params_, descriptor_->message_type()); +void RepeatedMessageFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!com.google.protobuf.nano.InternalNano.equals(\n" + " this.$name$, other.$name$)) {\n" + " return false;\n" + "}\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + printer->Print(variables_, + "result = 31 * result\n" + " + com.google.protobuf.nano.InternalNano.hashCode(this.$name$);\n"); } } // namespace javanano diff --git a/src/google/protobuf/compiler/javanano/javanano_message_field.h b/src/google/protobuf/compiler/javanano/javanano_message_field.h index 9807513..b724351 100644 --- a/src/google/protobuf/compiler/javanano/javanano_message_field.h +++ b/src/google/protobuf/compiler/javanano/javanano_message_field.h @@ -55,8 +55,8 @@ class MessageFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; - - string GetBoxedType() const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; private: const FieldDescriptor* descriptor_; @@ -77,8 +77,8 @@ class AccessorMessageFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; - - string GetBoxedType() const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; private: const FieldDescriptor* descriptor_; @@ -99,8 +99,8 @@ class RepeatedMessageFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; - - string GetBoxedType() const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; private: const FieldDescriptor* descriptor_; diff --git a/src/google/protobuf/compiler/javanano/javanano_params.h b/src/google/protobuf/compiler/javanano/javanano_params.h index 6e5379c..24aece3 100644 --- a/src/google/protobuf/compiler/javanano/javanano_params.h +++ b/src/google/protobuf/compiler/javanano/javanano_params.h @@ -61,6 +61,7 @@ class Params { bool java_enum_style_; bool optional_field_accessors_; bool use_reference_types_for_primitives_; + bool generate_equals_; public: Params(const string & base_name) : @@ -71,7 +72,8 @@ class Params { generate_has_(false), java_enum_style_(false), optional_field_accessors_(false), - use_reference_types_for_primitives_(false) { + use_reference_types_for_primitives_(false), + generate_equals_(false) { } const string& base_name() const { @@ -186,6 +188,13 @@ class Params { bool use_reference_types_for_primitives() const { return use_reference_types_for_primitives_; } + + void set_generate_equals(bool value) { + generate_equals_ = value; + } + bool generate_equals() const { + return generate_equals_; + } }; } // namespace javanano diff --git a/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc b/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc index 0f7512a..c0717e6 100644 --- a/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc +++ b/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc @@ -404,8 +404,102 @@ GenerateSerializedSizeCode(io::Printer* printer) const { } } -string PrimitiveFieldGenerator::GetBoxedType() const { - return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); +void PrimitiveFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + // We define equality as serialized form equality. If generate_has(), + // then if the field value equals the default value in both messages, + // but one's 'has' field is set and the other's is not, the serialized + // forms are different and we should return false. + JavaType java_type = GetJavaType(descriptor_); + if (java_type == JAVATYPE_BYTES) { + printer->Print(variables_, + "if (!java.util.Arrays.equals(this.$name$, other.$name$)"); + if (params_.generate_has()) { + printer->Print(variables_, + "\n" + " || (java.util.Arrays.equals(this.$name$, $default$)\n" + " && this.has$capitalized_name$ != other.has$capitalized_name$)"); + } + printer->Print(") {\n" + " return false;\n" + "}\n"); + } else if (java_type == JAVATYPE_STRING + || params_.use_reference_types_for_primitives()) { + printer->Print(variables_, + "if (this.$name$ == null) {\n" + " if (other.$name$ != null) {\n" + " return false;\n" + " }\n" + "} else if (!this.$name$.equals(other.$name$)"); + if (params_.generate_has()) { + printer->Print(variables_, + "\n" + " || (this.$name$.equals($default$)\n" + " && this.has$capitalized_name$ != other.has$capitalized_name$)"); + } + printer->Print(") {\n" + " return false;\n" + "}\n"); + } else { + printer->Print(variables_, + "if (this.$name$ != other.$name$"); + if (params_.generate_has()) { + printer->Print(variables_, + "\n" + " || (this.$name$ == $default$\n" + " && this.has$capitalized_name$ != other.has$capitalized_name$)"); + } + printer->Print(") {\n" + " return false;\n" + "}\n"); + } +} + +void PrimitiveFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + JavaType java_type = GetJavaType(descriptor_); + if (java_type == JAVATYPE_BYTES) { + printer->Print(variables_, + "result = 31 * result + java.util.Arrays.hashCode(this.$name$);\n"); + } else if (java_type == JAVATYPE_STRING + || params_.use_reference_types_for_primitives()) { + printer->Print(variables_, + "result = 31 * result\n" + " + (this.$name$ == null ? 0 : this.$name$.hashCode());\n"); + } else { + switch (java_type) { + // For all Java primitive types below, the hash codes match the + // results of BoxedType.valueOf(primitiveValue).hashCode(). + case JAVATYPE_INT: + printer->Print(variables_, + "result = 31 * result + this.$name$;\n"); + break; + case JAVATYPE_LONG: + printer->Print(variables_, + "result = 31 * result\n" + " + (int) (this.$name$ ^ (this.$name$ >>> 32));\n"); + break; + case JAVATYPE_FLOAT: + printer->Print(variables_, + "result = 31 * result\n" + " + java.lang.Float.floatToIntBits(this.$name$);\n"); + break; + case JAVATYPE_DOUBLE: + printer->Print(variables_, + "{\n" + " long v = java.lang.Double.doubleToLongBits(this.$name$);\n" + " result = 31 * result + (int) (v ^ (v >>> 32));\n" + "}\n"); + break; + case JAVATYPE_BOOLEAN: + printer->Print(variables_, + "result = 31 * result + (this.$name$ ? 1231 : 1237);\n"); + break; + default: + GOOGLE_LOG(ERROR) << "unknown java type for primitive field"; + break; + } + } } // =================================================================== @@ -483,8 +577,87 @@ GenerateSerializedSizeCode(io::Printer* printer) const { "}\n"); } -string AccessorPrimitiveFieldGenerator::GetBoxedType() const { - return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); +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: + case JAVATYPE_FLOAT: + case JAVATYPE_DOUBLE: + case JAVATYPE_BOOLEAN: + printer->Print(variables_, + "if ($different_has$\n" + " || $name$_ != other.$name$_) {\n" + " return false;\n" + "}\n"); + break; + case JAVATYPE_STRING: + // Accessor style would guarantee $name$_ non-null + printer->Print(variables_, + "if ($different_has$\n" + " || !$name$_.equals(other.$name$_)) {\n" + " return false;\n" + "}\n"); + break; + case JAVATYPE_BYTES: + // Accessor style would guarantee $name$_ non-null + printer->Print(variables_, + "if ($different_has$\n" + " || !java.util.Arrays.equals($name$_, other.$name$_)) {\n" + " return false;\n" + "}\n"); + break; + default: + GOOGLE_LOG(ERROR) << "unknown java type for primitive field"; + break; + } +} + +void AccessorPrimitiveFieldGenerator:: +GenerateHashCodeCode(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: + printer->Print(variables_, + "result = 31 * result + $name$_;\n"); + break; + case JAVATYPE_LONG: + printer->Print(variables_, + "result = 31 * result + (int) ($name$_ ^ ($name$_ >>> 32));\n"); + break; + case JAVATYPE_FLOAT: + printer->Print(variables_, + "result = 31 * result +\n" + " java.lang.Float.floatToIntBits($name$_);\n"); + break; + case JAVATYPE_DOUBLE: + printer->Print(variables_, + "{\n" + " long v = java.lang.Double.doubleToLongBits($name$_);\n" + " result = 31 * result + (int) (v ^ (v >>> 32));\n" + "}\n"); + break; + case JAVATYPE_BOOLEAN: + printer->Print(variables_, + "result = 31 * result + ($name$_ ? 1231 : 1237);\n"); + break; + case JAVATYPE_STRING: + // Accessor style would guarantee $name$_ non-null + printer->Print(variables_, + "result = 31 * result + $name$_.hashCode();\n"); + break; + case JAVATYPE_BYTES: + // Accessor style would guarantee $name$_ non-null + printer->Print(variables_, + "result = 31 * result + java.util.Arrays.hashCode($name$_);\n"); + break; + default: + GOOGLE_LOG(ERROR) << "unknown java type for primitive field"; + break; + } } // =================================================================== @@ -512,10 +685,46 @@ GenerateClearCode(io::Printer* printer) const { void RepeatedPrimitiveFieldGenerator:: GenerateMergingCode(io::Printer* printer) const { // First, figure out the length of the array, then parse. - if (descriptor_->options().packed()) { + 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"); + + if (GetJavaType(descriptor_) == JAVATYPE_BYTES) { + printer->Print(variables_, + "byte[][] newArray = new byte[i + arrayLength][];\n"); + } else { + printer->Print(variables_, + "$type$[] newArray = new $type$[i + arrayLength];\n"); + } + printer->Print(variables_, + "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.read$capitalized_type$();\n" + " input.readTag();\n" + "}\n" + "// Last one without readTag.\n" + "newArray[i] = input.read$capitalized_type$();\n" + "this.$name$ = newArray;\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateMergingCodeFromPacked(io::Printer* printer) const { + printer->Print( + "int length = input.readRawVarint32();\n" + "int limit = input.pushLimit(length);\n"); + + // If we know the elements will all be of the same size, the arrayLength + // can be calculated much more easily. However, FixedSize() returns 1 for + // repeated bool fields, which are guaranteed to have the fixed size of + // 1 byte per value only if we control the output. On the wire they can + // legally appear as variable-size integers, so we need to use the slow + // way for repeated bool fields. + if (descriptor_->type() == FieldDescriptor::TYPE_BOOL + || FixedSize(descriptor_->type()) == -1) { printer->Print(variables_, - "int length = input.readRawVarint32();\n" - "int limit = input.pushLimit(length);\n" "// First pass to compute array length.\n" "int arrayLength = 0;\n" "int startPos = input.getPosition();\n" @@ -523,54 +732,49 @@ GenerateMergingCode(io::Printer* printer) const { " input.read$capitalized_type$();\n" " arrayLength++;\n" "}\n" - "input.rewindToPosition(startPos);\n" - "int i = this.$name$ == null ? 0 : this.$name$.length;\n" - "$type$[] newArray = new $type$[i + arrayLength];\n" - "if (i != 0) {\n" - " java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n" - "}\n" - "for (; i < newArray.length; i++) {\n" - " newArray[i] = input.read$capitalized_type$();\n" - "}\n" - "this.$name$ = newArray;\n" - "input.popLimit(limit);\n"); + "input.rewindToPosition(startPos);\n"); } else { 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"); - - if (GetJavaType(descriptor_) == JAVATYPE_BYTES) { - printer->Print(variables_, - "byte[][] newArray = new byte[i + arrayLength][];\n"); - } else { - printer->Print(variables_, - "$type$[] newArray = new $type$[i + arrayLength];\n"); - } - printer->Print(variables_, - "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.read$capitalized_type$();\n" - " input.readTag();\n" - "}\n" - "// Last one without readTag.\n" - "newArray[i] = input.read$capitalized_type$();\n" - "this.$name$ = newArray;\n"); + "int arrayLength = length / $fixed_size$;\n"); } + + printer->Print(variables_, + "int i = this.$name$ == null ? 0 : this.$name$.length;\n" + "$type$[] newArray = new $type$[i + arrayLength];\n" + "if (i != 0) {\n" + " java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n" + "}\n" + "for (; i < newArray.length; i++) {\n" + " newArray[i] = input.read$capitalized_type$();\n" + "}\n" + "this.$name$ = newArray;\n" + "input.popLimit(limit);\n"); } void RepeatedPrimitiveFieldGenerator:: GenerateRepeatedDataSizeCode(io::Printer* printer) const { - // Creates a variable dataSize and puts the serialized size in - // there. - if (FixedSize(descriptor_->type()) == -1) { + // Creates a variable dataSize and puts the serialized size in there. + // If the element type is a Java reference type, also generates + // dataCount which stores the number of non-null elements in the field. + if (IsReferenceType(GetJavaType(descriptor_))) { printer->Print(variables_, + "int dataCount = 0;\n" "int dataSize = 0;\n" - "for ($type$ element : this.$name$) {\n" + "for (int i = 0; i < this.$name$.length; i++) {\n" + " $type$ element = this.$name$[i];\n" + " if (element != null) {\n" + " dataCount++;\n" + " dataSize += com.google.protobuf.nano.CodedOutputByteBufferNano\n" + " .compute$capitalized_type$SizeNoTag(element);\n" + " }\n" + "}\n"); + } else if (FixedSize(descriptor_->type()) == -1) { + printer->Print(variables_, + "int dataSize = 0;\n" + "for (int i = 0; i < this.$name$.length; i++) {\n" + " $type$ element = this.$name$[i];\n" " dataSize += com.google.protobuf.nano.CodedOutputByteBufferNano\n" - " .compute$capitalized_type$SizeNoTag(element);\n" + " .compute$capitalized_type$SizeNoTag(element);\n" "}\n"); } else { printer->Print(variables_, @@ -584,18 +788,26 @@ GenerateSerializationCode(io::Printer* printer) const { "if (this.$name$ != null && this.$name$.length > 0) {\n"); printer->Indent(); - if (descriptor_->options().packed()) { + if (descriptor_->is_packable() && descriptor_->options().packed()) { GenerateRepeatedDataSizeCode(printer); printer->Print(variables_, "output.writeRawVarint32($tag$);\n" "output.writeRawVarint32(dataSize);\n" - "for ($type$ element : this.$name$) {\n" - " output.write$capitalized_type$NoTag(element);\n" + "for (int i = 0; i < this.$name$.length; i++) {\n" + " output.write$capitalized_type$NoTag(this.$name$[i]);\n" + "}\n"); + } else if (IsReferenceType(GetJavaType(descriptor_))) { + printer->Print(variables_, + "for (int i = 0; i < this.$name$.length; i++) {\n" + " $type$ element = this.$name$[i];\n" + " if (element != null) {\n" + " output.write$capitalized_type$($number$, element);\n" + " }\n" "}\n"); } else { printer->Print(variables_, - "for ($type$ element : this.$name$) {\n" - " output.write$capitalized_type$($number$, element);\n" + "for (int i = 0; i < this.$name$.length; i++) {\n" + " output.write$capitalized_type$($number$, this.$name$[i]);\n" "}\n"); } @@ -613,14 +825,17 @@ GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print( "size += dataSize;\n"); - if (descriptor_->options().packed()) { + if (descriptor_->is_packable() && descriptor_->options().packed()) { printer->Print(variables_, "size += $tag_size$;\n" "size += com.google.protobuf.nano.CodedOutputByteBufferNano\n" - " .computeRawVarint32Size(dataSize);\n"); + " .computeRawVarint32Size(dataSize);\n"); + } else if (IsReferenceType(GetJavaType(descriptor_))) { + printer->Print(variables_, + "size += $tag_size$ * dataCount;\n"); } else { printer->Print(variables_, - "size += $tag_size$ * this.$name$.length;\n"); + "size += $tag_size$ * this.$name$.length;\n"); } printer->Outdent(); @@ -629,8 +844,20 @@ GenerateSerializedSizeCode(io::Printer* printer) const { "}\n"); } -string RepeatedPrimitiveFieldGenerator::GetBoxedType() const { - return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); +void RepeatedPrimitiveFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!com.google.protobuf.nano.InternalNano.equals(\n" + " this.$name$, other.$name$)) {\n" + " return false;\n" + "}\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + printer->Print(variables_, + "result = 31 * result\n" + " + com.google.protobuf.nano.InternalNano.hashCode(this.$name$);\n"); } } // namespace javanano diff --git a/src/google/protobuf/compiler/javanano/javanano_primitive_field.h b/src/google/protobuf/compiler/javanano/javanano_primitive_field.h index b42e2f3..d207535 100644 --- a/src/google/protobuf/compiler/javanano/javanano_primitive_field.h +++ b/src/google/protobuf/compiler/javanano/javanano_primitive_field.h @@ -55,8 +55,8 @@ class PrimitiveFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; - - string GetBoxedType() const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; private: void GenerateSerializationConditional(io::Printer* printer) const; @@ -79,8 +79,8 @@ class AccessorPrimitiveFieldGenerator : public FieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; - - string GetBoxedType() const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; private: const FieldDescriptor* descriptor_; @@ -98,10 +98,11 @@ class RepeatedPrimitiveFieldGenerator : public FieldGenerator { void GenerateMembers(io::Printer* printer) const; void GenerateClearCode(io::Printer* printer) const; void GenerateMergingCode(io::Printer* printer) const; + void GenerateMergingCodeFromPacked(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; - - string GetBoxedType() const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; private: void GenerateRepeatedDataSizeCode(io::Printer* printer) const; diff --git a/src/google/protobuf/io/coded_stream.h b/src/google/protobuf/io/coded_stream.h index dcbb0d4..e5f6161 100644 --- a/src/google/protobuf/io/coded_stream.h +++ b/src/google/protobuf/io/coded_stream.h @@ -521,7 +521,7 @@ class LIBPROTOBUF_EXPORT CodedInputStream { bool ReadStringFallback(string* buffer, int size); // Return the size of the buffer. - int BufferSize() const; + uint32 BufferSize() const; static const int kDefaultTotalBytesLimit = 64 << 20; // 64MB @@ -1031,7 +1031,7 @@ inline MessageFactory* CodedInputStream::GetExtensionFactory() { return extension_factory_; } -inline int CodedInputStream::BufferSize() const { +inline uint32 CodedInputStream::BufferSize() const { return buffer_end_ - buffer_; } diff --git a/src/google/protobuf/unittest_repeated_packables_nano.proto b/src/google/protobuf/unittest_repeated_packables_nano.proto new file mode 100644 index 0000000..1c78918 --- /dev/null +++ b/src/google/protobuf/unittest_repeated_packables_nano.proto @@ -0,0 +1,95 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: maxtroy@google.com (Max Cai) + +package protobuf_unittest; + +option java_package = "com.google.protobuf.nano"; +option java_outer_classname = "NanoRepeatedPackables"; + +enum Enum { + OPTION_ONE = 1; + OPTION_TWO = 2; +} + +// Two almost identical messages with all packable repeated field types. +// One with none marked as packed and the other all packed. For +// compatibility, they should be able to parse each other's serialized +// forms. + +message NonPacked { + + // All packable types, none marked as packed. + + repeated int32 int32s = 1; + repeated int64 int64s = 2; + repeated uint32 uint32s = 3; + repeated uint64 uint64s = 4; + repeated sint32 sint32s = 5; + repeated sint64 sint64s = 6; + repeated fixed32 fixed32s = 7; + repeated fixed64 fixed64s = 8; + repeated sfixed32 sfixed32s = 9; + repeated sfixed64 sfixed64s = 10; + repeated float floats = 11; + repeated double doubles = 12; + repeated bool bools = 13; + repeated Enum enums = 14; + + // Noise for testing merged deserialization. + optional int32 noise = 15; + +} + +message Packed { + + // All packable types, all matching the field numbers in NonPacked, + // all marked as packed. + + repeated int32 int32s = 1 [ packed = true ]; + repeated int64 int64s = 2 [ packed = true ]; + repeated uint32 uint32s = 3 [ packed = true ]; + repeated uint64 uint64s = 4 [ packed = true ]; + repeated sint32 sint32s = 5 [ packed = true ]; + repeated sint64 sint64s = 6 [ packed = true ]; + repeated fixed32 fixed32s = 7 [ packed = true ]; + repeated fixed64 fixed64s = 8 [ packed = true ]; + repeated sfixed32 sfixed32s = 9 [ packed = true ]; + repeated sfixed64 sfixed64s = 10 [ packed = true ]; + repeated float floats = 11 [ packed = true ]; + repeated double doubles = 12 [ packed = true ]; + repeated bool bools = 13 [ packed = true ]; + repeated Enum enums = 14 [ packed = true ]; + + // Noise for testing merged deserialization. + optional int32 noise = 15; + +} diff --git a/src/google/protobuf/wire_format_lite_inl.h b/src/google/protobuf/wire_format_lite_inl.h index d7b2c30..3f0d7f8 100644 --- a/src/google/protobuf/wire_format_lite_inl.h +++ b/src/google/protobuf/wire_format_lite_inl.h @@ -222,7 +222,7 @@ inline const uint8* WireFormatLite::ReadPrimitiveFromArray< } template <typename CType, enum WireFormatLite::FieldType DeclaredType> -inline bool WireFormatLite::ReadRepeatedPrimitive(int tag_size, +inline bool WireFormatLite::ReadRepeatedPrimitive(int, // tag_size, unused uint32 tag, io::CodedInputStream* input, RepeatedField<CType>* values) { |