diff --git a/java/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java b/java/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java index 769bb19cf1..88df38d7e0 100644 --- a/java/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java +++ b/java/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java @@ -168,14 +168,6 @@ public final class CodedOutputByteBufferNano { writeBytesNoTag(value); } - /** Write a {@code byte} field, including tag, to the stream. */ - public void writeByteArray(final int fieldNumber, final byte[] value) - throws IOException { - writeTag(fieldNumber, WireFormatNano.WIRETYPE_LENGTH_DELIMITED); - writeByteArrayNoTag(value); - } - - /** Write a {@code uint32} field, including tag, to the stream. */ public void writeUInt32(final int fieldNumber, final int value) throws IOException { @@ -321,12 +313,6 @@ public final class CodedOutputByteBufferNano { writeRawBytes(value); } - /** Write a {@code byte[]} field to the stream. */ - public void writeByteArrayNoTag(final byte [] value) throws IOException { - writeRawVarint32(value.length); - writeRawBytes(value); - } - /** Write a {@code uint32} field to the stream. */ public void writeUInt32NoTag(final int value) throws IOException { writeRawVarint32(value); @@ -466,15 +452,6 @@ public final class CodedOutputByteBufferNano { return computeTagSize(fieldNumber) + computeBytesSizeNoTag(value); } - /** - * Compute the number of bytes that would be needed to encode a - * {@code byte[]} field, including tag. - */ - public static int computeByteArraySize(final int fieldNumber, - final byte[] value) { - return computeTagSize(fieldNumber) + computeByteArraySizeNoTag(value); - } - /** * Compute the number of bytes that would be needed to encode a * {@code uint32} field, including tag. @@ -660,14 +637,6 @@ public final class CodedOutputByteBufferNano { return computeRawVarint32Size(value.length) + value.length; } - /** - * Compute the number of bytes that would be needed to encode a - * {@code byte[]} field. - */ - public static int computeByteArraySizeNoTag(final byte[] value) { - return computeRawVarint32Size(value.length) + value.length; - } - /** * Compute the number of bytes that would be needed to encode a * {@code uint32} field. diff --git a/java/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java b/java/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java index 066774d5bf..839f21c981 100644 --- a/java/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java +++ b/java/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java @@ -30,6 +30,7 @@ package com.google.protobuf.nano; +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -37,34 +38,81 @@ 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 { +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. + * can be accessed through the {@link #getExtension} and {@link #setExtension} methods. */ protected List unknownFieldData; @Override public int getSerializedSize() { - int size = WireFormatNano.computeWireSize(unknownFieldData); + int size = 0; + int unknownFieldCount = unknownFieldData == null ? 0 : unknownFieldData.size(); + for (int i = 0; i < unknownFieldCount; i++) { + UnknownFieldData unknownField = unknownFieldData.get(i); + size += CodedOutputByteBufferNano.computeRawVarint32Size(unknownField.tag); + size += unknownField.bytes.length; + } cachedSize = size; return size; } + @Override + public void writeTo(CodedOutputByteBufferNano output) throws IOException { + int unknownFieldCount = unknownFieldData == null ? 0 : unknownFieldData.size(); + for (int i = 0; i < unknownFieldCount; i++) { + UnknownFieldData unknownField = unknownFieldData.get(i); + output.writeRawVarint32(unknownField.tag); + output.writeRawBytes(unknownField.bytes); + } + } + /** * Gets the value stored in the specified extension of this message. */ - public T getExtension(Extension extension) { - return WireFormatNano.getExtension(extension, unknownFieldData); + public final T getExtension(Extension extension) { + return extension.getValueFrom(unknownFieldData); } /** * Sets the value of the specified extension of this message. */ - public void setExtension(Extension extension, T value) { + public final M setExtension(Extension extension, T value) { + unknownFieldData = extension.setValueTo(value, unknownFieldData); + + @SuppressWarnings("unchecked") // Generated code should guarantee type safety + M typedThis = (M) this; + return typedThis; + } + + /** + * Stores the binary data of an unknown field. + * + *

Generated messages will call this for unknown fields if the store_unknown_fields + * option is on. + * + *

Note that the tag might be a end-group tag (rather than the start of an unknown field) in + * which case we do not want to add an unknown field entry. + * + * @param input the input buffer. + * @param tag the tag of the field. + + * @return {@literal true} unless the tag is an end-group tag. + */ + protected final boolean storeUnknownField(CodedInputByteBufferNano input, int tag) + throws IOException { + int startPos = input.getPosition(); + if (!input.skipField(tag)) { + return false; // This wasn't an unknown field, it's an end-group tag. + } if (unknownFieldData == null) { unknownFieldData = new ArrayList(); } - WireFormatNano.setExtension(extension, value, unknownFieldData); + int endPos = input.getPosition(); + byte[] bytes = input.getData(startPos, endPos - startPos); + unknownFieldData.add(new UnknownFieldData(tag, bytes)); + return true; } -} \ No newline at end of file +} diff --git a/java/src/main/java/com/google/protobuf/nano/Extension.java b/java/src/main/java/com/google/protobuf/nano/Extension.java index 4512b01a5f..177a9ccc0a 100644 --- a/java/src/main/java/com/google/protobuf/nano/Extension.java +++ b/java/src/main/java/com/google/protobuf/nano/Extension.java @@ -30,85 +30,659 @@ package com.google.protobuf.nano; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; +import java.io.IOException; +import java.lang.reflect.Array; +import java.util.ArrayList; import java.util.List; /** * Represents an extension. * * @author bduff@google.com (Brian Duff) - * @param the type of the extension. + * @author maxtroy@google.com (Max Cai) + * @param the type of the extendable message this extension is for. + * @param the Java type of the extension; see {@link #clazz}. */ -public class Extension { - public final int fieldNumber; - public boolean isRepeatedField; - public Class fieldType; - public Class listType; - - private Extension(int fieldNumber, TypeLiteral type) { - this.fieldNumber = fieldNumber; - isRepeatedField = type.isList(); - fieldType = type.getTargetClass(); - listType = isRepeatedField ? type.getListType() : null; - } - - /** - * Creates a new instance of {@code Extension} for the specified {@code fieldNumber} and - * {@code type}. - */ - public static Extension create(int fieldNumber, TypeLiteral type) { - return new Extension(fieldNumber, type); - } - - /** - * Creates a new instance of {@code Extension} for the specified {@code fieldNumber} and - * {@code type}. This version is used for repeated fields. - */ - public static Extension> createRepeated(int fieldNumber, TypeLiteral> type) { - return new Extension>(fieldNumber, type); - } - - /** - * Represents a generic type literal. We can't typesafely reference a - * Class<List<Foo>>.class in Java, so we use this instead. - * See: http://gafter.blogspot.com/2006/12/super-type-tokens.html - * - *

Somewhat specialized because we only ever have a Foo or a List<Foo>. - */ - public static abstract class TypeLiteral { - private final Type type; - - protected TypeLiteral() { - Type superclass = getClass().getGenericSuperclass(); - if (superclass instanceof Class) { - throw new RuntimeException("Missing type parameter"); - } - this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0]; +public class Extension, T> { + + /* + * Because we typically only define message-typed extensions, the Extension class hierarchy is + * designed as follows, to allow a big amount of code in this file to be removed by ProGuard: + * + * Extension // ready to use for message/group typed extensions + * Δ + * | + * PrimitiveExtension // for primitive/enum typed extensions + */ + + public static final int TYPE_DOUBLE = 1; + public static final int TYPE_FLOAT = 2; + public static final int TYPE_INT64 = 3; + public static final int TYPE_UINT64 = 4; + public static final int TYPE_INT32 = 5; + public static final int TYPE_FIXED64 = 6; + public static final int TYPE_FIXED32 = 7; + public static final int TYPE_BOOL = 8; + public static final int TYPE_STRING = 9; + public static final int TYPE_GROUP = 10; + public static final int TYPE_MESSAGE = 11; + public static final int TYPE_BYTES = 12; + public static final int TYPE_UINT32 = 13; + public static final int TYPE_ENUM = 14; + public static final int TYPE_SFIXED32 = 15; + public static final int TYPE_SFIXED64 = 16; + public static final int TYPE_SINT32 = 17; + public static final int TYPE_SINT64 = 18; + + /** + * Creates an {@code Extension} of the given message type and tag number. + * Should be used by the generated code only. + * + * @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP} + */ + public static , T extends MessageNano> + Extension createMessageTyped(int type, Class clazz, int tag) { + return new Extension(type, clazz, tag, false); + } + + /** + * Creates a repeated {@code Extension} of the given message type and tag number. + * Should be used by the generated code only. + * + * @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP} + */ + public static , T extends MessageNano> + Extension createRepeatedMessageTyped(int type, Class clazz, int tag) { + return new Extension(type, clazz, tag, true); + } + + /** + * Creates an {@code Extension} of the given primitive type and tag number. + * Should be used by the generated code only. + * + * @param type one of {@code TYPE_*}, except {@link #TYPE_MESSAGE} and {@link #TYPE_GROUP} + * @param clazz the boxed Java type of this extension + */ + public static , T> + Extension createPrimitiveTyped(int type, Class clazz, int tag) { + return new PrimitiveExtension(type, clazz, tag, false, 0, 0); + } + + /** + * Creates a repeated {@code Extension} of the given primitive type and tag number. + * Should be used by the generated code only. + * + * @param type one of {@code TYPE_*}, except {@link #TYPE_MESSAGE} and {@link #TYPE_GROUP} + * @param clazz the Java array type of this extension, with an unboxed component type + */ + public static , T> + Extension createRepeatedPrimitiveTyped( + int type, Class clazz, int tag, int nonPackedTag, int packedTag) { + return new PrimitiveExtension(type, clazz, tag, true, nonPackedTag, packedTag); + } + + /** + * Protocol Buffer type of this extension; one of the {@code TYPE_} constants. + */ + protected final int type; + + /** + * Java type of this extension. For a singular extension, this is the boxed Java type for the + * Protocol Buffer {@link #type}; for a repeated extension, this is an array type whose + * component type is the unboxed Java type for {@link #type}. For example, for a singular + * {@code int32}/{@link #TYPE_INT32} extension, this equals {@code Integer.class}; for a + * repeated {@code int32} extension, this equals {@code int[].class}. + */ + protected final Class clazz; + + /** + * Tag number of this extension. + */ + protected final int tag; + + /** + * Whether this extension is repeated. + */ + protected final boolean repeated; + + private Extension(int type, Class clazz, int tag, boolean repeated) { + this.type = type; + this.clazz = clazz; + this.tag = tag; + this.repeated = repeated; + } + + protected boolean isMatch(int unknownDataTag) { + // This implementation is for message/group extensions. + return unknownDataTag == tag; + } + + /** + * Returns the value of this extension stored in the given list of unknown fields, or + * {@code null} if no unknown fields matches this extension. + */ + final T getValueFrom(List unknownFields) { + if (unknownFields == null) { + return null; + } + + if (repeated) { + // For repeated extensions, read all matching unknown fields in their original order. + List resultList = new ArrayList(); + for (int i = 0; i < unknownFields.size(); i++) { + UnknownFieldData data = unknownFields.get(i); + if (isMatch(data.tag) && data.bytes.length != 0) { + readDataInto(data, resultList); + } + } + + int resultSize = resultList.size(); + if (resultSize == 0) { + return null; + } + + T result = clazz.cast(Array.newInstance(clazz.getComponentType(), resultSize)); + for (int i = 0; i < resultSize; i++) { + Array.set(result, i, resultList.get(i)); + } + return result; + } else { + // For singular extensions, get the last piece of data stored under this extension. + UnknownFieldData lastData = null; + for (int i = unknownFields.size() - 1; lastData == null && i >= 0; i--) { + UnknownFieldData data = unknownFields.get(i); + if (isMatch(data.tag) && data.bytes.length != 0) { + lastData = data; + } + } + + if (lastData == null) { + return null; + } + + return clazz.cast(readData(CodedInputByteBufferNano.newInstance(lastData.bytes))); + } + } + + protected Object readData(CodedInputByteBufferNano input) { + // This implementation is for message/group extensions. + Class messageType = repeated ? clazz.getComponentType() : clazz; + try { + switch (type) { + case TYPE_GROUP: + MessageNano group = (MessageNano) messageType.newInstance(); + input.readGroup(group, WireFormatNano.getTagFieldNumber(tag)); + return group; + case TYPE_MESSAGE: + MessageNano message = (MessageNano) messageType.newInstance(); + input.readMessage(message); + return message; + default: + throw new IllegalArgumentException("Unknown type " + type); + } + } catch (InstantiationException e) { + throw new IllegalArgumentException( + "Error creating instance of class " + messageType, e); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException( + "Error creating instance of class " + messageType, e); + } catch (IOException e) { + throw new IllegalArgumentException("Error reading extension field", e); + } + } + + protected void readDataInto(UnknownFieldData data, List resultList) { + // This implementation is for message/group extensions. + resultList.add(readData(CodedInputByteBufferNano.newInstance(data.bytes))); } /** - * If the generic type is a list, returns {@code true}. + * Sets the value of this extension to the given list of unknown fields. This removes any + * previously stored data matching this extension. + * + * @param value The value of this extension, or {@code null} to clear this extension from the + * unknown fields. + * @return The same {@code unknownFields} list, or a new list storing the extension value if + * the argument was null. */ - private boolean isList() { - return type instanceof ParameterizedType; + final List setValueTo(T value, List unknownFields) { + if (unknownFields != null) { + // Delete all data matching this extension + for (int i = unknownFields.size() - 1; i >= 0; i--) { + if (isMatch(unknownFields.get(i).tag)) { + unknownFields.remove(i); + } + } + } + + if (value != null) { + if (unknownFields == null) { + unknownFields = new ArrayList(); + } + if (repeated) { + writeDataInto(value, unknownFields); + } else { + unknownFields.add(writeData(value)); + } + } + + // After deletion or no-op addition (due to 'value' being an array of empty or + // null-only elements), unknownFields may be empty. Discard the ArrayList if so. + return (unknownFields.size() == 0) ? null : unknownFields; } - @SuppressWarnings("unchecked") - private Class getListType() { - return (Class) ((ParameterizedType) type).getRawType(); + protected UnknownFieldData writeData(Object value) { + // This implementation is for message/group extensions. + byte[] data; + try { + switch (type) { + case TYPE_GROUP: + MessageNano groupValue = (MessageNano) value; + int fieldNumber = WireFormatNano.getTagFieldNumber(tag); + data = new byte[CodedOutputByteBufferNano.computeGroupSizeNoTag(groupValue) + + CodedOutputByteBufferNano.computeTagSize(fieldNumber)]; + CodedOutputByteBufferNano out = CodedOutputByteBufferNano.newInstance(data); + out.writeGroupNoTag(groupValue); + // The endgroup tag must be included in the data payload. + out.writeTag(fieldNumber, WireFormatNano.WIRETYPE_END_GROUP); + break; + case TYPE_MESSAGE: + MessageNano messageValue = (MessageNano) value; + data = new byte[ + CodedOutputByteBufferNano.computeMessageSizeNoTag(messageValue)]; + CodedOutputByteBufferNano.newInstance(data).writeMessageNoTag(messageValue); + break; + default: + throw new IllegalArgumentException("Unknown type " + type); + } + } catch (IOException e) { + // Should not happen + throw new IllegalStateException(e); + } + return new UnknownFieldData(tag, data); + } + + protected void writeDataInto(T array, List unknownFields) { + // This implementation is for non-packed extensions. + int arrayLength = Array.getLength(array); + for (int i = 0; i < arrayLength; i++) { + Object element = Array.get(array, i); + if (element != null) { + unknownFields.add(writeData(element)); + } + } } /** - * If the generic type is a list, returns the type of element in the list. Otherwise, - * returns the actual type. + * Represents an extension of a primitive (including enum) type. If there is no primitive + * extensions, this subclass will be removable by ProGuard. */ - @SuppressWarnings("unchecked") - private Class getTargetClass() { - if (isList()) { - return (Class) ((ParameterizedType) type).getActualTypeArguments()[0]; - } - return (Class) type; + private static class PrimitiveExtension, T> + extends Extension { + + /** + * Tag of a piece of non-packed data from the wire compatible with this extension. + */ + private final int nonPackedTag; + + /** + * Tag of a piece of packed data from the wire compatible with this extension. + * 0 if the type of this extension is not packable. + */ + private final int packedTag; + + public PrimitiveExtension(int type, Class clazz, int tag, boolean repeated, + int nonPackedTag, int packedTag) { + super(type, clazz, tag, repeated); + this.nonPackedTag = nonPackedTag; + this.packedTag = packedTag; + } + + @Override + protected boolean isMatch(int unknownDataTag) { + if (repeated) { + return unknownDataTag == nonPackedTag || unknownDataTag == packedTag; + } else { + return unknownDataTag == tag; + } + } + + @Override + protected Object readData(CodedInputByteBufferNano input) { + try { + switch (type) { + case TYPE_DOUBLE: + return input.readDouble(); + case TYPE_FLOAT: + return input.readFloat(); + case TYPE_INT64: + return input.readInt64(); + case TYPE_UINT64: + return input.readUInt64(); + case TYPE_INT32: + return input.readInt32(); + case TYPE_FIXED64: + return input.readFixed64(); + case TYPE_FIXED32: + return input.readFixed32(); + case TYPE_BOOL: + return input.readBool(); + case TYPE_STRING: + return input.readString(); + case TYPE_BYTES: + return input.readBytes(); + case TYPE_UINT32: + return input.readUInt32(); + case TYPE_ENUM: + return input.readEnum(); + case TYPE_SFIXED32: + return input.readSFixed32(); + case TYPE_SFIXED64: + return input.readSFixed64(); + case TYPE_SINT32: + return input.readSInt32(); + case TYPE_SINT64: + return input.readSInt64(); + default: + throw new IllegalArgumentException("Unknown type " + type); + } + } catch (IOException e) { + throw new IllegalArgumentException("Error reading extension field", e); + } + } + + @Override + protected void readDataInto(UnknownFieldData data, List resultList) { + // This implementation is for primitive typed extensions, + // which can read both packed and non-packed data. + if (data.tag == nonPackedTag) { + resultList.add(readData(CodedInputByteBufferNano.newInstance(data.bytes))); + } else { + CodedInputByteBufferNano buffer = CodedInputByteBufferNano.newInstance(data.bytes); + try { + buffer.pushLimit(buffer.readRawVarint32()); // length limit + } catch (IOException e) { + throw new IllegalArgumentException("Error reading extension field", e); + } + while (!buffer.isAtEnd()) { + resultList.add(readData(buffer)); + } + } + } + + @Override + protected final UnknownFieldData writeData(Object value) { + byte[] data; + try { + switch (type) { + case TYPE_DOUBLE: + Double doubleValue = (Double) value; + data = new byte[ + CodedOutputByteBufferNano.computeDoubleSizeNoTag(doubleValue)]; + CodedOutputByteBufferNano.newInstance(data).writeDoubleNoTag(doubleValue); + break; + case TYPE_FLOAT: + Float floatValue = (Float) value; + data = new byte[ + CodedOutputByteBufferNano.computeFloatSizeNoTag(floatValue)]; + CodedOutputByteBufferNano.newInstance(data).writeFloatNoTag(floatValue); + break; + case TYPE_INT64: + Long int64Value = (Long) value; + data = new byte[ + CodedOutputByteBufferNano.computeInt64SizeNoTag(int64Value)]; + CodedOutputByteBufferNano.newInstance(data).writeInt64NoTag(int64Value); + break; + case TYPE_UINT64: + Long uint64Value = (Long) value; + data = new byte[ + CodedOutputByteBufferNano.computeUInt64SizeNoTag(uint64Value)]; + CodedOutputByteBufferNano.newInstance(data).writeUInt64NoTag(uint64Value); + break; + case TYPE_INT32: + Integer int32Value = (Integer) value; + data = new byte[ + CodedOutputByteBufferNano.computeInt32SizeNoTag(int32Value)]; + CodedOutputByteBufferNano.newInstance(data).writeInt32NoTag(int32Value); + break; + case TYPE_FIXED64: + Long fixed64Value = (Long) value; + data = new byte[ + CodedOutputByteBufferNano.computeFixed64SizeNoTag(fixed64Value)]; + CodedOutputByteBufferNano.newInstance(data).writeFixed64NoTag(fixed64Value); + break; + case TYPE_FIXED32: + Integer fixed32Value = (Integer) value; + data = new byte[ + CodedOutputByteBufferNano.computeFixed32SizeNoTag(fixed32Value)]; + CodedOutputByteBufferNano.newInstance(data).writeFixed32NoTag(fixed32Value); + break; + case TYPE_BOOL: + Boolean boolValue = (Boolean) value; + data = new byte[CodedOutputByteBufferNano.computeBoolSizeNoTag(boolValue)]; + CodedOutputByteBufferNano.newInstance(data).writeBoolNoTag(boolValue); + break; + case TYPE_STRING: + String stringValue = (String) value; + data = new byte[ + CodedOutputByteBufferNano.computeStringSizeNoTag(stringValue)]; + CodedOutputByteBufferNano.newInstance(data).writeStringNoTag(stringValue); + break; + case TYPE_BYTES: + byte[] bytesValue = (byte[]) value; + data = new byte[ + CodedOutputByteBufferNano.computeBytesSizeNoTag(bytesValue)]; + CodedOutputByteBufferNano.newInstance(data).writeBytesNoTag(bytesValue); + break; + case TYPE_UINT32: + Integer uint32Value = (Integer) value; + data = new byte[ + CodedOutputByteBufferNano.computeUInt32SizeNoTag(uint32Value)]; + CodedOutputByteBufferNano.newInstance(data).writeUInt32NoTag(uint32Value); + break; + case TYPE_ENUM: + Integer enumValue = (Integer) value; + data = new byte[CodedOutputByteBufferNano.computeEnumSizeNoTag(enumValue)]; + CodedOutputByteBufferNano.newInstance(data).writeEnumNoTag(enumValue); + break; + case TYPE_SFIXED32: + Integer sfixed32Value = (Integer) value; + data = new byte[ + CodedOutputByteBufferNano.computeSFixed32SizeNoTag(sfixed32Value)]; + CodedOutputByteBufferNano.newInstance(data) + .writeSFixed32NoTag(sfixed32Value); + break; + case TYPE_SFIXED64: + Long sfixed64Value = (Long) value; + data = new byte[ + CodedOutputByteBufferNano.computeSFixed64SizeNoTag(sfixed64Value)]; + CodedOutputByteBufferNano.newInstance(data) + .writeSFixed64NoTag(sfixed64Value); + break; + case TYPE_SINT32: + Integer sint32Value = (Integer) value; + data = new byte[ + CodedOutputByteBufferNano.computeSInt32SizeNoTag(sint32Value)]; + CodedOutputByteBufferNano.newInstance(data).writeSInt32NoTag(sint32Value); + break; + case TYPE_SINT64: + Long sint64Value = (Long) value; + data = new byte[ + CodedOutputByteBufferNano.computeSInt64SizeNoTag(sint64Value)]; + CodedOutputByteBufferNano.newInstance(data).writeSInt64NoTag(sint64Value); + break; + default: + throw new IllegalArgumentException("Unknown type " + type); + } + } catch (IOException e) { + // Should not happen + throw new IllegalStateException(e); + } + return new UnknownFieldData(tag, data); + } + + @Override + protected void writeDataInto(T array, List unknownFields) { + if (tag == nonPackedTag) { + // Use base implementation for non-packed data + super.writeDataInto(array, unknownFields); + } else if (tag == packedTag) { + // Packed. Note that the array element type is guaranteed to be primitive, so there + // won't be any null elements, so no null check in this block. First get data size. + int arrayLength = Array.getLength(array); + int dataSize = 0; + switch (type) { + case TYPE_BOOL: + // Bools are stored as int32 but just as 0 or 1, so 1 byte each. + dataSize = arrayLength; + break; + case TYPE_FIXED32: + case TYPE_SFIXED32: + case TYPE_FLOAT: + dataSize = arrayLength * CodedOutputByteBufferNano.LITTLE_ENDIAN_32_SIZE; + break; + case TYPE_FIXED64: + case TYPE_SFIXED64: + case TYPE_DOUBLE: + dataSize = arrayLength * CodedOutputByteBufferNano.LITTLE_ENDIAN_64_SIZE; + break; + case TYPE_INT32: + for (int i = 0; i < arrayLength; i++) { + dataSize += CodedOutputByteBufferNano.computeInt32SizeNoTag( + Array.getInt(array, i)); + } + break; + case TYPE_SINT32: + for (int i = 0; i < arrayLength; i++) { + dataSize += CodedOutputByteBufferNano.computeSInt32SizeNoTag( + Array.getInt(array, i)); + } + break; + case TYPE_UINT32: + for (int i = 0; i < arrayLength; i++) { + dataSize += CodedOutputByteBufferNano.computeUInt32SizeNoTag( + Array.getInt(array, i)); + } + break; + case TYPE_INT64: + for (int i = 0; i < arrayLength; i++) { + dataSize += CodedOutputByteBufferNano.computeInt64SizeNoTag( + Array.getLong(array, i)); + } + break; + case TYPE_SINT64: + for (int i = 0; i < arrayLength; i++) { + dataSize += CodedOutputByteBufferNano.computeSInt64SizeNoTag( + Array.getLong(array, i)); + } + break; + case TYPE_UINT64: + for (int i = 0; i < arrayLength; i++) { + dataSize += CodedOutputByteBufferNano.computeUInt64SizeNoTag( + Array.getLong(array, i)); + } + break; + case TYPE_ENUM: + for (int i = 0; i < arrayLength; i++) { + dataSize += CodedOutputByteBufferNano.computeEnumSizeNoTag( + Array.getInt(array, i)); + } + break; + default: + throw new IllegalArgumentException("Unexpected non-packable type " + type); + } + + // Then construct payload. + int payloadSize = + dataSize + CodedOutputByteBufferNano.computeRawVarint32Size(dataSize); + byte[] data = new byte[payloadSize]; + CodedOutputByteBufferNano output = CodedOutputByteBufferNano.newInstance(data); + try { + output.writeRawVarint32(dataSize); + switch (type) { + case TYPE_BOOL: + for (int i = 0; i < arrayLength; i++) { + output.writeBoolNoTag(Array.getBoolean(array, i)); + } + break; + case TYPE_FIXED32: + for (int i = 0; i < arrayLength; i++) { + output.writeFixed32NoTag(Array.getInt(array, i)); + } + break; + case TYPE_SFIXED32: + for (int i = 0; i < arrayLength; i++) { + output.writeSFixed32NoTag(Array.getInt(array, i)); + } + break; + case TYPE_FLOAT: + for (int i = 0; i < arrayLength; i++) { + output.writeFloatNoTag(Array.getFloat(array, i)); + } + break; + case TYPE_FIXED64: + for (int i = 0; i < arrayLength; i++) { + output.writeFixed64NoTag(Array.getLong(array, i)); + } + break; + case TYPE_SFIXED64: + for (int i = 0; i < arrayLength; i++) { + output.writeSFixed64NoTag(Array.getLong(array, i)); + } + break; + case TYPE_DOUBLE: + for (int i = 0; i < arrayLength; i++) { + output.writeDoubleNoTag(Array.getDouble(array, i)); + } + break; + case TYPE_INT32: + for (int i = 0; i < arrayLength; i++) { + output.writeInt32NoTag(Array.getInt(array, i)); + } + break; + case TYPE_SINT32: + for (int i = 0; i < arrayLength; i++) { + output.writeSInt32NoTag(Array.getInt(array, i)); + } + break; + case TYPE_UINT32: + for (int i = 0; i < arrayLength; i++) { + output.writeUInt32NoTag(Array.getInt(array, i)); + } + break; + case TYPE_INT64: + for (int i = 0; i < arrayLength; i++) { + output.writeInt64NoTag(Array.getLong(array, i)); + } + break; + case TYPE_SINT64: + for (int i = 0; i < arrayLength; i++) { + output.writeSInt64NoTag(Array.getLong(array, i)); + } + break; + case TYPE_UINT64: + for (int i = 0; i < arrayLength; i++) { + output.writeUInt64NoTag(Array.getLong(array, i)); + } + break; + case TYPE_ENUM: + for (int i = 0; i < arrayLength; i++) { + output.writeEnumNoTag(Array.getInt(array, i)); + } + break; + default: + throw new IllegalArgumentException("Unpackable type " + type); + } + } catch (IOException e) { + // Should not happen. + throw new IllegalStateException(e); + } + unknownFields.add(new UnknownFieldData(tag, data)); + } else { + throw new IllegalArgumentException("Unexpected repeated extension tag " + tag + + ", unequal to both non-packed variant " + nonPackedTag + + " and packed variant " + packedTag); + } + } } - } } 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 e95c51417c..82dc6cca56 100644 --- a/java/src/main/java/com/google/protobuf/nano/MessageNano.java +++ b/java/src/main/java/com/google/protobuf/nano/MessageNano.java @@ -67,16 +67,20 @@ public abstract class MessageNano { } /** - * Serializes the message and writes it to {@code output}. This does not - * flush or close the stream. + * Serializes the message and writes it to {@code output}. + * + * @param output the output to receive the serialized form. + * @throws IOException if an error occurred writing to {@code output}. */ - abstract public void writeTo(CodedOutputByteBufferNano output) throws java.io.IOException; + public void writeTo(CodedOutputByteBufferNano output) throws IOException { + // Does nothing by default. Overridden by subclasses which have data to write. + } /** * Parse {@code input} as a message of this type and merge it with the * message being built. */ - abstract public MessageNano mergeFrom(final CodedInputByteBufferNano input) throws IOException; + public abstract MessageNano mergeFrom(CodedInputByteBufferNano input) throws IOException; /** * Serialize to a byte array. @@ -95,9 +99,8 @@ public abstract class MessageNano { * write more than length bytes OutOfSpaceException will be thrown * and if length bytes are not written then IllegalStateException * is thrown. - * @return byte array with the serialized data. */ - public static final void toByteArray(MessageNano msg, byte [] data, int offset, int length) { + public static final void toByteArray(MessageNano msg, byte[] data, int offset, int length) { try { final CodedOutputByteBufferNano output = CodedOutputByteBufferNano.newInstance(data, offset, length); diff --git a/java/src/main/java/com/google/protobuf/nano/WireFormatNano.java b/java/src/main/java/com/google/protobuf/nano/WireFormatNano.java index 301ff1db77..1ff8f06fb6 100644 --- a/java/src/main/java/com/google/protobuf/nano/WireFormatNano.java +++ b/java/src/main/java/com/google/protobuf/nano/WireFormatNano.java @@ -31,9 +31,6 @@ package com.google.protobuf.nano; import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; /** * This class is used internally by the Protocol Buffer library and generated @@ -75,21 +72,6 @@ public final class WireFormatNano { return (fieldNumber << TAG_TYPE_BITS) | wireType; } - // Field numbers for feilds in MessageSet wire format. - static final int MESSAGE_SET_ITEM = 1; - static final int MESSAGE_SET_TYPE_ID = 2; - static final int MESSAGE_SET_MESSAGE = 3; - - // Tag numbers. - static final int MESSAGE_SET_ITEM_TAG = - makeTag(MESSAGE_SET_ITEM, WIRETYPE_START_GROUP); - static final int MESSAGE_SET_ITEM_END_TAG = - makeTag(MESSAGE_SET_ITEM, WIRETYPE_END_GROUP); - static final int MESSAGE_SET_TYPE_ID_TAG = - makeTag(MESSAGE_SET_TYPE_ID, WIRETYPE_VARINT); - static final int MESSAGE_SET_MESSAGE_TAG = - makeTag(MESSAGE_SET_MESSAGE, WIRETYPE_LENGTH_DELIMITED); - public static final int EMPTY_INT_ARRAY[] = {}; public static final long EMPTY_LONG_ARRAY[] = {}; public static final float EMPTY_FLOAT_ARRAY[] = {}; @@ -113,35 +95,6 @@ public final class WireFormatNano { return input.skipField(tag); } - /** - * Stores the binary data of an unknown field. - * - *

Generated messages will call this for unknown fields if the store_unknown_fields - * option is on. - * - *

Note that the tag might be a end-group tag (rather than the start of an unknown field) in - * which case we do not want to add an unknown field entry. - * - * @param data a Collection in which to store the data. - * @param input the input buffer. - * @param tag the tag of the field. - - * @return {@literal true} unless the tag is an end-group tag. - */ - public static boolean storeUnknownField( - final List data, - final CodedInputByteBufferNano input, - final int tag) throws IOException { - int startPos = input.getPosition(); - if (!input.skipField(tag)) { - return false; // This wasn't an unknown field, it's an end-group tag. - } - int endPos = input.getPosition(); - byte[] bytes = input.getData(startPos, endPos - startPos); - data.add(new UnknownFieldData(tag, bytes)); - return true; - } - /** * Computes the array length of a repeated field. We assume that in the common case repeated * fields are contiguously serialized but we still correctly handle interspersed values of a @@ -172,193 +125,4 @@ public final class WireFormatNano { return arrayLength; } - /** - * Decodes the value of an extension. - */ - public static T getExtension(Extension extension, List unknownFields) { - if (unknownFields == null) { - return null; - } - List dataForField = new ArrayList(); - for (UnknownFieldData data : unknownFields) { - if (getTagFieldNumber(data.tag) == extension.fieldNumber) { - dataForField.add(data); - } - } - if (dataForField.isEmpty()) { - return null; - } - - if (extension.isRepeatedField) { - List result = new ArrayList(dataForField.size()); - for (UnknownFieldData data : dataForField) { - result.add(readData(extension.fieldType, data.bytes)); - } - return extension.listType.cast(result); - } - - // Normal fields. Note that the protobuf docs require us to handle multiple instances - // of the same field even for fields that are not repeated. - UnknownFieldData lastData = dataForField.get(dataForField.size() - 1); - return readData(extension.fieldType, lastData.bytes); - } - - /** - * Reads (extension) data of the specified type from the specified byte array. - * - * @throws IllegalArgumentException if an error occurs while reading the data. - */ - private static T readData(Class clazz, byte[] data) { - if (data.length == 0) { - return null; - } - CodedInputByteBufferNano buffer = CodedInputByteBufferNano.newInstance(data); - try { - if (clazz == String.class) { - return clazz.cast(buffer.readString()); - } else if (clazz == Integer.class) { - return clazz.cast(buffer.readInt32()); - } else if (clazz == Long.class) { - return clazz.cast(buffer.readInt64()); - } else if (clazz == Boolean.class) { - return clazz.cast(buffer.readBool()); - } else if (clazz == Float.class) { - return clazz.cast(buffer.readFloat()); - } else if (clazz == Double.class) { - return clazz.cast(buffer.readDouble()); - } else if (clazz == byte[].class) { - return clazz.cast(buffer.readBytes()); - } else if (MessageNano.class.isAssignableFrom(clazz)) { - try { - MessageNano message = (MessageNano) clazz.newInstance(); - buffer.readMessage(message); - return clazz.cast(message); - } catch (IllegalAccessException e) { - throw new IllegalArgumentException("Error creating instance of class " + clazz, e); - } catch (InstantiationException e) { - throw new IllegalArgumentException("Error creating instance of class " + clazz, e); - } - } else { - throw new IllegalArgumentException("Unhandled extension field type: " + clazz); - } - } catch (IOException e) { - throw new IllegalArgumentException("Error reading extension field", e); - } - } - - public static void setExtension(Extension extension, T value, - List unknownFields) { - // First, remove all unknown fields with this tag. - for (Iterator i = unknownFields.iterator(); i.hasNext();) { - UnknownFieldData data = i.next(); - if (extension.fieldNumber == getTagFieldNumber(data.tag)) { - i.remove(); - } - } - if (value == null) { - return; - } - // Repeated field. - if (value instanceof List) { - for (Object item : (List) value) { - unknownFields.add(write(extension.fieldNumber, item)); - } - } else { - unknownFields.add(write(extension.fieldNumber, value)); - } - } - - /** - * Writes extension data and returns an {@link UnknownFieldData} containing - * bytes and a tag. - * - * @throws IllegalArgumentException if an error occurs while writing. - */ - private static UnknownFieldData write(int fieldNumber, Object object) { - byte[] data; - int tag; - Class clazz = object.getClass(); - try { - if (clazz == String.class) { - String str = (String) object; - data = new byte[CodedOutputByteBufferNano.computeStringSizeNoTag(str)]; - CodedOutputByteBufferNano.newInstance(data).writeStringNoTag(str); - tag = makeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); - } else if (clazz == Integer.class) { - Integer integer = (Integer) object; - data = new byte[CodedOutputByteBufferNano.computeInt32SizeNoTag(integer)]; - CodedOutputByteBufferNano.newInstance(data).writeInt32NoTag(integer); - tag = makeTag(fieldNumber, WIRETYPE_VARINT); - } else if (clazz == Long.class) { - Long longValue = (Long) object; - data = new byte[CodedOutputByteBufferNano.computeInt64SizeNoTag(longValue)]; - CodedOutputByteBufferNano.newInstance(data).writeInt64NoTag(longValue); - tag = makeTag(fieldNumber, WIRETYPE_VARINT); - } else if (clazz == Boolean.class) { - Boolean boolValue = (Boolean) object; - data = new byte[CodedOutputByteBufferNano.computeBoolSizeNoTag(boolValue)]; - CodedOutputByteBufferNano.newInstance(data).writeBoolNoTag(boolValue); - tag = makeTag(fieldNumber, WIRETYPE_VARINT); - } else if (clazz == Float.class) { - Float floatValue = (Float) object; - data = new byte[CodedOutputByteBufferNano.computeFloatSizeNoTag(floatValue)]; - CodedOutputByteBufferNano.newInstance(data).writeFloatNoTag(floatValue); - tag = makeTag(fieldNumber, WIRETYPE_FIXED32); - } else if (clazz == Double.class) { - Double doubleValue = (Double) object; - data = new byte[CodedOutputByteBufferNano.computeDoubleSizeNoTag(doubleValue)]; - CodedOutputByteBufferNano.newInstance(data).writeDoubleNoTag(doubleValue); - tag = makeTag(fieldNumber, WIRETYPE_FIXED64); - } else if (clazz == byte[].class) { - byte[] byteArrayValue = (byte[]) object; - data = new byte[CodedOutputByteBufferNano.computeByteArraySizeNoTag(byteArrayValue)]; - CodedOutputByteBufferNano.newInstance(data).writeByteArrayNoTag(byteArrayValue); - tag = makeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); - } else if (MessageNano.class.isAssignableFrom(clazz)) { - MessageNano messageValue = (MessageNano) object; - - int messageSize = messageValue.getSerializedSize(); - int delimiterSize = CodedOutputByteBufferNano.computeRawVarint32Size(messageSize); - data = new byte[messageSize + delimiterSize]; - CodedOutputByteBufferNano buffer = CodedOutputByteBufferNano.newInstance(data); - buffer.writeRawVarint32(messageSize); - buffer.writeRawBytes(MessageNano.toByteArray(messageValue)); - tag = makeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); - } else { - throw new IllegalArgumentException("Unhandled extension field type: " + clazz); - } - } catch (IOException e) { - throw new IllegalArgumentException(e); - } - return new UnknownFieldData(tag, data); - } - - /** - * Given a set of unknown field data, compute the wire size. - */ - public static int computeWireSize(List unknownFields) { - if (unknownFields == null) { - return 0; - } - int size = 0; - for (UnknownFieldData unknownField : unknownFields) { - size += CodedOutputByteBufferNano.computeRawVarint32Size(unknownField.tag); - size += unknownField.bytes.length; - } - return size; - } - - /** - * Write unknown fields. - */ - public static void writeUnknownFields(List unknownFields, - CodedOutputByteBufferNano outBuffer) throws IOException { - if (unknownFields == null) { - return; - } - for (UnknownFieldData data : unknownFields) { - outBuffer.writeTag(getTagFieldNumber(data.tag), getTagWireType(data.tag)); - outBuffer.writeRawBytes(data.bytes); - } - } } diff --git a/java/src/test/java/com/google/protobuf/NanoTest.java b/java/src/test/java/com/google/protobuf/NanoTest.java index 32a331e361..9987cacede 100644 --- a/java/src/test/java/com/google/protobuf/NanoTest.java +++ b/java/src/test/java/com/google/protobuf/NanoTest.java @@ -52,6 +52,9 @@ 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.PackedExtensions; +import com.google.protobuf.nano.RepeatedExtensions; +import com.google.protobuf.nano.SingularExtensions; import com.google.protobuf.nano.TestRepeatedMergeNano; import com.google.protobuf.nano.UnittestImportNano; import com.google.protobuf.nano.UnittestMultipleNano; @@ -61,10 +64,8 @@ import com.google.protobuf.nano.UnittestSingleNano.SingleMessageNano; import junit.framework.TestCase; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.List; /** * Test nano runtime. @@ -520,12 +521,12 @@ public class NanoTest extends TestCase { byte [] serialized = MessageNano.toByteArray(msg); MessageWithGroup parsed = MessageWithGroup.parseFrom(serialized); - assertTrue(msg.group != null); - assertEquals(1, msg.group.a); + assertEquals(1, parsed.group.a); byte [] serialized2 = MessageNano.toByteArray(parsed); - assertEquals(serialized2.length, serialized.length); + assertEquals(serialized.length, serialized2.length); MessageWithGroup parsed2 = MessageWithGroup.parseFrom(serialized2); + assertEquals(1, parsed2.group.a); } public void testNanoOptionalNestedMessage() throws Exception { @@ -2222,6 +2223,7 @@ public class NanoTest extends TestCase { */ public void testNanoSingle() throws Exception { SingleMessageNano msg = new SingleMessageNano(); + assertNotNull(msg); } /** @@ -2597,12 +2599,14 @@ public class NanoTest extends TestCase { msg.defaultFloatNan = 0; byte[] result = MessageNano.toByteArray(msg); int msgSerializedSize = msg.getSerializedSize(); + assertTrue(result.length == msgSerializedSize); assertTrue(msgSerializedSize > 3); msg.defaultDoubleNan = Double.NaN; msg.defaultFloatNan = Float.NaN; result = MessageNano.toByteArray(msg); msgSerializedSize = msg.getSerializedSize(); + assertEquals(3, result.length); assertEquals(3, msgSerializedSize); } @@ -2735,57 +2739,163 @@ public class NanoTest extends TestCase { public void testExtensions() throws Exception { Extensions.ExtendableMessage message = new Extensions.ExtendableMessage(); message.field = 5; - message.setExtension(Extensions.someString, "Hello World!"); - message.setExtension(Extensions.someBool, true); - message.setExtension(Extensions.someInt, 42); - message.setExtension(Extensions.someLong, 124234234234L); - message.setExtension(Extensions.someFloat, 42.0f); - message.setExtension(Extensions.someDouble, 422222.0); - message.setExtension(Extensions.someEnum, Extensions.FIRST_VALUE); - AnotherMessage another = new AnotherMessage(); - another.string = "Foo"; - another.value = true; - message.setExtension(Extensions.someMessage, another); - - message.setExtension(Extensions.someRepeatedString, list("a", "bee", "seeya")); - message.setExtension(Extensions.someRepeatedBool, list(true, false, true)); - message.setExtension(Extensions.someRepeatedInt, list(4, 8, 15, 16, 23, 42)); - message.setExtension(Extensions.someRepeatedLong, list(4L, 8L, 15L, 16L, 23L, 42L)); - message.setExtension(Extensions.someRepeatedFloat, list(1.0f, 3.0f)); - message.setExtension(Extensions.someRepeatedDouble, list(55.133, 3.14159)); - message.setExtension(Extensions.someRepeatedEnum, list(Extensions.FIRST_VALUE, - Extensions.SECOND_VALUE)); - AnotherMessage second = new AnotherMessage(); - second.string = "Whee"; - second.value = false; - message.setExtension(Extensions.someRepeatedMessage, list(another, second)); + int[] int32s = {1, 2}; + int[] uint32s = {3, 4}; + int[] sint32s = {-5, -6}; + long[] int64s = {7, 8}; + long[] uint64s = {9, 10}; + long[] sint64s = {-11, -12}; + int[] fixed32s = {13, 14}; + int[] sfixed32s = {-15, -16}; + long[] fixed64s = {17, 18}; + long[] sfixed64s = {-19, -20}; + boolean[] bools = {true, false}; + float[] floats = {2.1f, 2.2f}; + double[] doubles = {2.3, 2.4}; + int[] enums = {Extensions.SECOND_VALUE, Extensions.FIRST_VALUE}; + String[] strings = {"vijfentwintig", "twenty-six"}; + byte[][] bytess = {{2, 7}, {2, 8}}; + AnotherMessage another1 = new AnotherMessage(); + another1.string = "er shi jiu"; + another1.value = false; + AnotherMessage another2 = new AnotherMessage(); + another2.string = "trente"; + another2.value = true; + AnotherMessage[] messages = {another1, another2}; + RepeatedExtensions.RepeatedGroup group1 = new RepeatedExtensions.RepeatedGroup(); + group1.a = 31; + RepeatedExtensions.RepeatedGroup group2 = new RepeatedExtensions.RepeatedGroup(); + group2.a = 32; + RepeatedExtensions.RepeatedGroup[] groups = {group1, group2}; + message.setExtension(RepeatedExtensions.repeatedInt32, int32s); + message.setExtension(RepeatedExtensions.repeatedUint32, uint32s); + message.setExtension(RepeatedExtensions.repeatedSint32, sint32s); + message.setExtension(RepeatedExtensions.repeatedInt64, int64s); + message.setExtension(RepeatedExtensions.repeatedUint64, uint64s); + message.setExtension(RepeatedExtensions.repeatedSint64, sint64s); + message.setExtension(RepeatedExtensions.repeatedFixed32, fixed32s); + message.setExtension(RepeatedExtensions.repeatedSfixed32, sfixed32s); + message.setExtension(RepeatedExtensions.repeatedFixed64, fixed64s); + message.setExtension(RepeatedExtensions.repeatedSfixed64, sfixed64s); + message.setExtension(RepeatedExtensions.repeatedBool, bools); + message.setExtension(RepeatedExtensions.repeatedFloat, floats); + message.setExtension(RepeatedExtensions.repeatedDouble, doubles); + message.setExtension(RepeatedExtensions.repeatedEnum, enums); + message.setExtension(RepeatedExtensions.repeatedString, strings); + message.setExtension(RepeatedExtensions.repeatedBytes, bytess); + message.setExtension(RepeatedExtensions.repeatedMessage, messages); + message.setExtension(RepeatedExtensions.repeatedGroup, groups); byte[] data = MessageNano.toByteArray(message); - - Extensions.ExtendableMessage deserialized = Extensions.ExtendableMessage.parseFrom(data); - assertEquals(5, deserialized.field); - assertEquals("Hello World!", deserialized.getExtension(Extensions.someString)); - assertEquals(Boolean.TRUE, deserialized.getExtension(Extensions.someBool)); - assertEquals(Integer.valueOf(42), deserialized.getExtension(Extensions.someInt)); - assertEquals(Long.valueOf(124234234234L), deserialized.getExtension(Extensions.someLong)); - assertEquals(Float.valueOf(42.0f), deserialized.getExtension(Extensions.someFloat)); - assertEquals(Double.valueOf(422222.0), deserialized.getExtension(Extensions.someDouble)); - assertEquals(Integer.valueOf(Extensions.FIRST_VALUE), - deserialized.getExtension(Extensions.someEnum)); - assertEquals(another.string, deserialized.getExtension(Extensions.someMessage).string); - assertEquals(another.value, deserialized.getExtension(Extensions.someMessage).value); - assertEquals(list("a", "bee", "seeya"), deserialized.getExtension(Extensions.someRepeatedString)); - assertEquals(list(true, false, true), deserialized.getExtension(Extensions.someRepeatedBool)); - assertEquals(list(4, 8, 15, 16, 23, 42), deserialized.getExtension(Extensions.someRepeatedInt)); - assertEquals(list(4L, 8L, 15L, 16L, 23L, 42L), deserialized.getExtension(Extensions.someRepeatedLong)); - assertEquals(list(1.0f, 3.0f), deserialized.getExtension(Extensions.someRepeatedFloat)); - assertEquals(list(55.133, 3.14159), deserialized.getExtension(Extensions.someRepeatedDouble)); - assertEquals(list(Extensions.FIRST_VALUE, - Extensions.SECOND_VALUE), deserialized.getExtension(Extensions.someRepeatedEnum)); - assertEquals("Foo", deserialized.getExtension(Extensions.someRepeatedMessage).get(0).string); - assertEquals(true, deserialized.getExtension(Extensions.someRepeatedMessage).get(0).value); - assertEquals("Whee", deserialized.getExtension(Extensions.someRepeatedMessage).get(1).string); - assertEquals(false, deserialized.getExtension(Extensions.someRepeatedMessage).get(1).value); + message = Extensions.ExtendableMessage.parseFrom(data); + assertEquals(5, message.field); + + // Test reading back using SingularExtensions: the retrieved value should equal the last + // in each array. + assertEquals(int32s[1], (int) message.getExtension(SingularExtensions.someInt32)); + assertEquals(uint32s[1], (int) message.getExtension(SingularExtensions.someUint32)); + assertEquals(sint32s[1], (int) message.getExtension(SingularExtensions.someSint32)); + assertEquals(int64s[1], (long) message.getExtension(SingularExtensions.someInt64)); + assertEquals(uint64s[1], (long) message.getExtension(SingularExtensions.someUint64)); + assertEquals(sint64s[1], (long) message.getExtension(SingularExtensions.someSint64)); + assertEquals(fixed32s[1], (int) message.getExtension(SingularExtensions.someFixed32)); + assertEquals(sfixed32s[1], (int) message.getExtension(SingularExtensions.someSfixed32)); + assertEquals(fixed64s[1], (long) message.getExtension(SingularExtensions.someFixed64)); + assertEquals(sfixed64s[1], (long) message.getExtension(SingularExtensions.someSfixed64)); + assertEquals(bools[1], (boolean) message.getExtension(SingularExtensions.someBool)); + assertEquals(floats[1], (float) message.getExtension(SingularExtensions.someFloat)); + assertEquals(doubles[1], (double) message.getExtension(SingularExtensions.someDouble)); + assertEquals(enums[1], (int) message.getExtension(SingularExtensions.someEnum)); + assertEquals(strings[1], message.getExtension(SingularExtensions.someString)); + assertTrue(Arrays.equals(bytess[1], message.getExtension(SingularExtensions.someBytes))); + AnotherMessage deserializedMessage = message.getExtension(SingularExtensions.someMessage); + assertEquals(another2.string, deserializedMessage.string); + assertEquals(another2.value, deserializedMessage.value); + assertEquals(group2.a, message.getExtension(SingularExtensions.someGroup).a); + + // Test reading back using RepeatedExtensions: the arrays should be equal. + assertTrue(Arrays.equals(int32s, message.getExtension(RepeatedExtensions.repeatedInt32))); + assertTrue(Arrays.equals(uint32s, message.getExtension(RepeatedExtensions.repeatedUint32))); + assertTrue(Arrays.equals(sint32s, message.getExtension(RepeatedExtensions.repeatedSint32))); + assertTrue(Arrays.equals(int64s, message.getExtension(RepeatedExtensions.repeatedInt64))); + assertTrue(Arrays.equals(uint64s, message.getExtension(RepeatedExtensions.repeatedUint64))); + assertTrue(Arrays.equals(sint64s, message.getExtension(RepeatedExtensions.repeatedSint64))); + assertTrue(Arrays.equals(fixed32s, message.getExtension(RepeatedExtensions.repeatedFixed32))); + assertTrue(Arrays.equals(sfixed32s, message.getExtension(RepeatedExtensions.repeatedSfixed32))); + assertTrue(Arrays.equals(fixed64s, message.getExtension(RepeatedExtensions.repeatedFixed64))); + assertTrue(Arrays.equals(sfixed64s, message.getExtension(RepeatedExtensions.repeatedSfixed64))); + assertTrue(Arrays.equals(bools, message.getExtension(RepeatedExtensions.repeatedBool))); + assertTrue(Arrays.equals(floats, message.getExtension(RepeatedExtensions.repeatedFloat))); + assertTrue(Arrays.equals(doubles, message.getExtension(RepeatedExtensions.repeatedDouble))); + assertTrue(Arrays.equals(enums, message.getExtension(RepeatedExtensions.repeatedEnum))); + assertTrue(Arrays.equals(strings, message.getExtension(RepeatedExtensions.repeatedString))); + byte[][] deserializedRepeatedBytes = message.getExtension(RepeatedExtensions.repeatedBytes); + assertEquals(2, deserializedRepeatedBytes.length); + assertTrue(Arrays.equals(bytess[0], deserializedRepeatedBytes[0])); + assertTrue(Arrays.equals(bytess[1], deserializedRepeatedBytes[1])); + AnotherMessage[] deserializedRepeatedMessage = + message.getExtension(RepeatedExtensions.repeatedMessage); + assertEquals(2, deserializedRepeatedMessage.length); + assertEquals(another1.string, deserializedRepeatedMessage[0].string); + assertEquals(another1.value, deserializedRepeatedMessage[0].value); + assertEquals(another2.string, deserializedRepeatedMessage[1].string); + assertEquals(another2.value, deserializedRepeatedMessage[1].value); + RepeatedExtensions.RepeatedGroup[] deserializedRepeatedGroup = + message.getExtension(RepeatedExtensions.repeatedGroup); + assertEquals(2, deserializedRepeatedGroup.length); + assertEquals(group1.a, deserializedRepeatedGroup[0].a); + assertEquals(group2.a, deserializedRepeatedGroup[1].a); + + // Test reading back using PackedExtensions: the arrays should be equal, even the fields + // are non-packed. + assertTrue(Arrays.equals(int32s, message.getExtension(PackedExtensions.packedInt32))); + assertTrue(Arrays.equals(uint32s, message.getExtension(PackedExtensions.packedUint32))); + assertTrue(Arrays.equals(sint32s, message.getExtension(PackedExtensions.packedSint32))); + assertTrue(Arrays.equals(int64s, message.getExtension(PackedExtensions.packedInt64))); + assertTrue(Arrays.equals(uint64s, message.getExtension(PackedExtensions.packedUint64))); + assertTrue(Arrays.equals(sint64s, message.getExtension(PackedExtensions.packedSint64))); + assertTrue(Arrays.equals(fixed32s, message.getExtension(PackedExtensions.packedFixed32))); + assertTrue(Arrays.equals(sfixed32s, message.getExtension(PackedExtensions.packedSfixed32))); + assertTrue(Arrays.equals(fixed64s, message.getExtension(PackedExtensions.packedFixed64))); + assertTrue(Arrays.equals(sfixed64s, message.getExtension(PackedExtensions.packedSfixed64))); + assertTrue(Arrays.equals(bools, message.getExtension(PackedExtensions.packedBool))); + assertTrue(Arrays.equals(floats, message.getExtension(PackedExtensions.packedFloat))); + assertTrue(Arrays.equals(doubles, message.getExtension(PackedExtensions.packedDouble))); + assertTrue(Arrays.equals(enums, message.getExtension(PackedExtensions.packedEnum))); + + // Now set the packable extension values using PackedExtensions so they're serialized packed. + message.setExtension(PackedExtensions.packedInt32, int32s); + message.setExtension(PackedExtensions.packedUint32, uint32s); + message.setExtension(PackedExtensions.packedSint32, sint32s); + message.setExtension(PackedExtensions.packedInt64, int64s); + message.setExtension(PackedExtensions.packedUint64, uint64s); + message.setExtension(PackedExtensions.packedSint64, sint64s); + message.setExtension(PackedExtensions.packedFixed32, fixed32s); + message.setExtension(PackedExtensions.packedSfixed32, sfixed32s); + message.setExtension(PackedExtensions.packedFixed64, fixed64s); + message.setExtension(PackedExtensions.packedSfixed64, sfixed64s); + message.setExtension(PackedExtensions.packedBool, bools); + message.setExtension(PackedExtensions.packedFloat, floats); + message.setExtension(PackedExtensions.packedDouble, doubles); + message.setExtension(PackedExtensions.packedEnum, enums); + + // And read back using non-packed RepeatedExtensions. + byte[] data2 = MessageNano.toByteArray(message); + message = MessageNano.mergeFrom(new Extensions.ExtendableMessage(), data2); + assertTrue(Arrays.equals(int32s, message.getExtension(RepeatedExtensions.repeatedInt32))); + assertTrue(Arrays.equals(uint32s, message.getExtension(RepeatedExtensions.repeatedUint32))); + assertTrue(Arrays.equals(sint32s, message.getExtension(RepeatedExtensions.repeatedSint32))); + assertTrue(Arrays.equals(int64s, message.getExtension(RepeatedExtensions.repeatedInt64))); + assertTrue(Arrays.equals(uint64s, message.getExtension(RepeatedExtensions.repeatedUint64))); + assertTrue(Arrays.equals(sint64s, message.getExtension(RepeatedExtensions.repeatedSint64))); + assertTrue(Arrays.equals(fixed32s, message.getExtension(RepeatedExtensions.repeatedFixed32))); + assertTrue(Arrays.equals(sfixed32s, message.getExtension(RepeatedExtensions.repeatedSfixed32))); + assertTrue(Arrays.equals(fixed64s, message.getExtension(RepeatedExtensions.repeatedFixed64))); + assertTrue(Arrays.equals(sfixed64s, message.getExtension(RepeatedExtensions.repeatedSfixed64))); + assertTrue(Arrays.equals(bools, message.getExtension(RepeatedExtensions.repeatedBool))); + assertTrue(Arrays.equals(floats, message.getExtension(RepeatedExtensions.repeatedFloat))); + assertTrue(Arrays.equals(doubles, message.getExtension(RepeatedExtensions.repeatedDouble))); + assertTrue(Arrays.equals(enums, message.getExtension(RepeatedExtensions.repeatedEnum))); } public void testUnknownFields() throws Exception { @@ -3462,13 +3572,4 @@ public class NanoTest extends TestCase { } return sb.toString(); } - - private List list(T first, T... remaining) { - List list = new ArrayList(); - list.add(first); - for (T item : remaining) { - list.add(item); - } - return list; - } } diff --git a/src/google/protobuf/compiler/javanano/javanano_extension.cc b/src/google/protobuf/compiler/javanano/javanano_extension.cc index 0bc9c9d186..754ed550d3 100644 --- a/src/google/protobuf/compiler/javanano/javanano_extension.cc +++ b/src/google/protobuf/compiler/javanano/javanano_extension.cc @@ -42,28 +42,84 @@ namespace compiler { namespace javanano { using internal::WireFormat; +using internal::WireFormatLite; + +namespace { + +const char* GetTypeConstantName(const FieldDescriptor::Type type) { + switch (type) { + case FieldDescriptor::TYPE_INT32 : return "TYPE_INT32" ; + case FieldDescriptor::TYPE_UINT32 : return "TYPE_UINT32" ; + case FieldDescriptor::TYPE_SINT32 : return "TYPE_SINT32" ; + case FieldDescriptor::TYPE_FIXED32 : return "TYPE_FIXED32" ; + case FieldDescriptor::TYPE_SFIXED32: return "TYPE_SFIXED32"; + case FieldDescriptor::TYPE_INT64 : return "TYPE_INT64" ; + case FieldDescriptor::TYPE_UINT64 : return "TYPE_UINT64" ; + case FieldDescriptor::TYPE_SINT64 : return "TYPE_SINT64" ; + case FieldDescriptor::TYPE_FIXED64 : return "TYPE_FIXED64" ; + case FieldDescriptor::TYPE_SFIXED64: return "TYPE_SFIXED64"; + case FieldDescriptor::TYPE_FLOAT : return "TYPE_FLOAT" ; + case FieldDescriptor::TYPE_DOUBLE : return "TYPE_DOUBLE" ; + case FieldDescriptor::TYPE_BOOL : return "TYPE_BOOL" ; + case FieldDescriptor::TYPE_STRING : return "TYPE_STRING" ; + case FieldDescriptor::TYPE_BYTES : return "TYPE_BYTES" ; + case FieldDescriptor::TYPE_ENUM : return "TYPE_ENUM" ; + case FieldDescriptor::TYPE_GROUP : return "TYPE_GROUP" ; + case FieldDescriptor::TYPE_MESSAGE : return "TYPE_MESSAGE" ; + + // No default because we want the compiler to complain if any new + // types are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + +} // namespace void SetVariables(const FieldDescriptor* descriptor, const Params params, map* variables) { - (*variables)["name"] = - RenameJavaKeywords(UnderscoresToCamelCase(descriptor)); - (*variables)["number"] = SimpleItoa(descriptor->number()); (*variables)["extends"] = ClassName(params, descriptor->containing_type()); - - string type; + (*variables)["name"] = RenameJavaKeywords(UnderscoresToCamelCase(descriptor)); + bool repeated = descriptor->is_repeated(); + (*variables)["repeated"] = repeated ? "Repeated" : ""; + (*variables)["type"] = GetTypeConstantName(descriptor->type()); JavaType java_type = GetJavaType(descriptor->type()); - switch (java_type) { - case JAVATYPE_ENUM: - type = "java.lang.Integer"; - break; - case JAVATYPE_MESSAGE: - type = ClassName(params, descriptor->message_type()); - break; - default: - type = BoxedPrimitiveTypeName(java_type); - break; + string tag = SimpleItoa(WireFormat::MakeTag(descriptor)); + if (java_type == JAVATYPE_MESSAGE) { + (*variables)["ext_type"] = "MessageTyped"; + string message_type = ClassName(params, descriptor->message_type()); + if (repeated) { + message_type += "[]"; + } + (*variables)["class"] = message_type; + // For message typed extensions, tags_params contains a single tag + // for both singular and repeated cases. + (*variables)["tag_params"] = tag; + } else { + (*variables)["ext_type"] = "PrimitiveTyped"; + if (!repeated) { + (*variables)["class"] = BoxedPrimitiveTypeName(java_type); + (*variables)["tag_params"] = tag; + } else { + (*variables)["class"] = PrimitiveTypeName(java_type) + "[]"; + if (!descriptor->is_packable()) { + // Non-packable: nonPackedTag == tag, packedTag == 0 + (*variables)["tag_params"] = tag + ", " + tag + ", 0"; + } else if (descriptor->options().packed()) { + // Packable and packed: tag == packedTag + string non_packed_tag = SimpleItoa(WireFormatLite::MakeTag( + descriptor->number(), + WireFormat::WireTypeForFieldType(descriptor->type()))); + (*variables)["tag_params"] = tag + ", " + non_packed_tag + ", " + tag; + } else { + // Packable and not packed: tag == nonPackedTag + string packed_tag = SimpleItoa(WireFormatLite::MakeTag( + descriptor->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED)); + (*variables)["tag_params"] = tag + ", " + tag + ", " + packed_tag; + } + } } - (*variables)["type"] = type; } ExtensionGenerator:: @@ -75,21 +131,16 @@ ExtensionGenerator(const FieldDescriptor* descriptor, const Params& params) ExtensionGenerator::~ExtensionGenerator() {} void ExtensionGenerator::Generate(io::Printer* printer) const { - if (descriptor_->is_repeated()) { - printer->Print(variables_, - "\n" - "// extends $extends$\n" - "public static final com.google.protobuf.nano.Extension> $name$ = \n" - " com.google.protobuf.nano.Extension.createRepeated($number$,\n" - " new com.google.protobuf.nano.Extension.TypeLiteral>(){});\n"); - } else { - printer->Print(variables_, - "\n" - "// extends $extends$\n" - "public static final com.google.protobuf.nano.Extension<$type$> $name$ =\n" - " com.google.protobuf.nano.Extension.create($number$,\n" - " new com.google.protobuf.nano.Extension.TypeLiteral<$type$>(){});\n"); - } + printer->Print("\n"); + PrintFieldComment(printer, descriptor_); + printer->Print(variables_, + "public static final com.google.protobuf.nano.Extension<\n" + " $extends$,\n" + " $class$> $name$ =\n" + " com.google.protobuf.nano.Extension.create$repeated$$ext_type$(\n" + " com.google.protobuf.nano.Extension.$type$,\n" + " $class$.class,\n" + " $tag_params$);\n"); } } // namespace javanano diff --git a/src/google/protobuf/compiler/javanano/javanano_helpers.cc b/src/google/protobuf/compiler/javanano/javanano_helpers.cc index ede4c6f56a..e8326a4251 100644 --- a/src/google/protobuf/compiler/javanano/javanano_helpers.cc +++ b/src/google/protobuf/compiler/javanano/javanano_helpers.cc @@ -264,6 +264,22 @@ string FieldDefaultConstantName(const FieldDescriptor *field) { return "_" + RenameJavaKeywords(UnderscoresToCamelCase(field)) + "Default"; } +void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field) { + // We don't want to print group bodies so we cut off after the first line + // (the second line for extensions). + string def = field->DebugString(); + string::size_type first_line_end = def.find_first_of('\n'); + printer->Print("// $def$\n", + "def", def.substr(0, first_line_end)); + if (field->is_extension()) { + string::size_type second_line_start = first_line_end + 1; + string::size_type second_line_length = + def.find('\n', second_line_start) - second_line_start; + printer->Print("// $def$\n", + "def", def.substr(second_line_start, second_line_length)); + } +} + JavaType GetJavaType(FieldDescriptor::Type field_type) { switch (field_type) { case FieldDescriptor::TYPE_INT32: @@ -310,7 +326,27 @@ JavaType GetJavaType(FieldDescriptor::Type field_type) { return JAVATYPE_INT; } -const char* BoxedPrimitiveTypeName(JavaType type) { +string PrimitiveTypeName(JavaType type) { + switch (type) { + case JAVATYPE_INT : return "int"; + case JAVATYPE_LONG : return "long"; + case JAVATYPE_FLOAT : return "float"; + case JAVATYPE_DOUBLE : return "double"; + case JAVATYPE_BOOLEAN: return "boolean"; + case JAVATYPE_STRING : return "java.lang.String"; + case JAVATYPE_BYTES : return "byte[]"; + case JAVATYPE_ENUM : return "int"; + case JAVATYPE_MESSAGE: return NULL; + + // No default because we want the compiler to complain if any new + // JavaTypes are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + +string BoxedPrimitiveTypeName(JavaType type) { switch (type) { case JAVATYPE_INT : return "java.lang.Integer"; case JAVATYPE_LONG : return "java.lang.Long"; diff --git a/src/google/protobuf/compiler/javanano/javanano_helpers.h b/src/google/protobuf/compiler/javanano/javanano_helpers.h index 753a4bd4cf..886bff88fa 100644 --- a/src/google/protobuf/compiler/javanano/javanano_helpers.h +++ b/src/google/protobuf/compiler/javanano/javanano_helpers.h @@ -39,6 +39,7 @@ #include #include #include +#include namespace google { namespace protobuf { @@ -111,6 +112,9 @@ string FieldConstantName(const FieldDescriptor *field); string FieldDefaultConstantName(const FieldDescriptor *field); +// Print the field's proto-syntax definition as a comment. +void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field); + enum JavaType { JAVATYPE_INT, JAVATYPE_LONG, @@ -129,10 +133,12 @@ inline JavaType GetJavaType(const FieldDescriptor* field) { return GetJavaType(field->type()); } +string PrimitiveTypeName(JavaType type); + // Get the fully-qualified class name for a boxed primitive type, e.g. // "java.lang.Integer" for JAVATYPE_INT. Returns NULL for enum and message // types. -const char* BoxedPrimitiveTypeName(JavaType type); +string BoxedPrimitiveTypeName(JavaType type); string EmptyArrayName(const Params& params, const FieldDescriptor* field); diff --git a/src/google/protobuf/compiler/javanano/javanano_message.cc b/src/google/protobuf/compiler/javanano/javanano_message.cc index c09670a180..008bec26bd 100644 --- a/src/google/protobuf/compiler/javanano/javanano_message.cc +++ b/src/google/protobuf/compiler/javanano/javanano_message.cc @@ -54,14 +54,6 @@ using internal::WireFormatLite; namespace { -void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field) { - // Print the field's proto-syntax definition as a comment. We don't want to - // print group bodies so we cut off after the first line. - string def = field->DebugString(); - printer->Print("// $def$\n", - "def", def.substr(0, def.find_first_of('\n'))); -} - struct FieldOrderingByNumber { inline bool operator()(const FieldDescriptor* a, const FieldDescriptor* b) const { @@ -82,13 +74,6 @@ const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor) { return fields; } -// Get an identifier that uniquely identifies this type within the file. -// This is used to declare static variables related to this type at the -// outermost file scope. -string UniqueFileScopeIdentifier(const Descriptor* descriptor) { - return "static_" + StringReplace(descriptor->full_name(), ".", "_", true); -} - } // namespace // =================================================================== @@ -149,7 +134,8 @@ void MessageGenerator::Generate(io::Printer* printer) { } if (params_.store_unknown_fields()) { printer->Print( - " com.google.protobuf.nano.ExtendableMessageNano {\n"); + " com.google.protobuf.nano.ExtendableMessageNano<$classname$> {\n", + "classname", descriptor_->name()); } else { printer->Print( " com.google.protobuf.nano.MessageNano {\n"); @@ -285,22 +271,20 @@ void MessageGenerator::Generate(io::Printer* printer) { void MessageGenerator:: GenerateMessageSerializationMethods(io::Printer* printer) { + // Rely on the parent implementations of writeTo() and getSerializedSize() + // if there are no fields to serialize in this message. + if (descriptor_->field_count() == 0) { + return; + } + scoped_array sorted_fields( SortFieldsByNumber(descriptor_)); - // writeTo only throws an exception if it contains one or more fields to write - if (descriptor_->field_count() > 0 || params_.store_unknown_fields()) { - printer->Print( - "\n" - "@Override\n" - "public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output)\n" - " throws java.io.IOException {\n"); - } else { - printer->Print( - "\n" - "@Override\n" - "public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output) {\n"); - } + printer->Print( + "\n" + "@Override\n" + "public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output)\n" + " throws java.io.IOException {\n"); printer->Indent(); // Output the fields in sorted order @@ -308,36 +292,31 @@ GenerateMessageSerializationMethods(io::Printer* printer) { GenerateSerializeOneField(printer, sorted_fields[i]); } - // Write unknown fields. - if (params_.store_unknown_fields()) { - printer->Print( - "com.google.protobuf.nano.WireFormatNano.writeUnknownFields(\n" - " unknownFieldData, output);\n"); - } + // The parent implementation will write any unknown fields if necessary. + printer->Print( + "super.writeTo(output);\n"); printer->Outdent(); printer->Print("}\n"); - // 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(); - - for (int i = 0; i < descriptor_->field_count(); i++) { - field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer); - } + // The parent implementation will get the serialized size for unknown + // fields if necessary. + printer->Print( + "\n" + "@Override\n" + "public int getSerializedSize() {\n" + " int size = super.getSerializedSize();\n"); + printer->Indent(); - printer->Outdent(); - printer->Print( - " cachedSize = size;\n" - " return size;\n" - "}\n"); + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer); } + + printer->Outdent(); + printer->Print( + " cachedSize = size;\n" + " return size;\n" + "}\n"); } void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) { @@ -371,12 +350,7 @@ void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) { printer->Indent(); if (params_.store_unknown_fields()) { printer->Print( - "if (unknownFieldData == null) {\n" - " unknownFieldData =\n" - " new java.util.ArrayList();\n" - "}\n" - "if (!com.google.protobuf.nano.WireFormatNano.storeUnknownField(\n" - " unknownFieldData, input, tag)) {\n" + "if (!storeUnknownField(input, tag)) {\n" " return this;\n" "}\n"); } else { diff --git a/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc b/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc index e044e8909d..a3bc3a84ee 100644 --- a/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc +++ b/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc @@ -54,26 +54,6 @@ using internal::WireFormatLite; namespace { -const char* PrimitiveTypeName(JavaType type) { - switch (type) { - case JAVATYPE_INT : return "int"; - case JAVATYPE_LONG : return "long"; - case JAVATYPE_FLOAT : return "float"; - case JAVATYPE_DOUBLE : return "double"; - case JAVATYPE_BOOLEAN: return "boolean"; - case JAVATYPE_STRING : return "java.lang.String"; - case JAVATYPE_BYTES : return "byte[]"; - case JAVATYPE_ENUM : return NULL; - case JAVATYPE_MESSAGE: return NULL; - - // No default because we want the compiler to complain if any new - // JavaTypes are added. - } - - GOOGLE_LOG(FATAL) << "Can't get here."; - return NULL; -} - bool IsReferenceType(JavaType type) { switch (type) { case JAVATYPE_INT : return false; diff --git a/src/google/protobuf/unittest_extension_nano.proto b/src/google/protobuf/unittest_extension_nano.proto index 104cfa749a..0a775f4c65 100644 --- a/src/google/protobuf/unittest_extension_nano.proto +++ b/src/google/protobuf/unittest_extension_nano.proto @@ -18,31 +18,14 @@ message AnotherMessage { optional bool value = 2; } -extend ExtendableMessage { - optional string some_string = 10; - optional int32 some_int = 11; - optional int64 some_long = 12; - optional float some_float = 13; - optional double some_double = 14; - optional bool some_bool = 15; - optional AnEnum some_enum = 16; - optional AnotherMessage some_message = 17; - repeated string some_repeated_string = 18; - repeated int32 some_repeated_int = 19; - repeated int64 some_repeated_long = 20; - repeated float some_repeated_float = 21; - repeated double some_repeated_double = 22; - repeated bool some_repeated_bool = 23; - repeated AnEnum some_repeated_enum = 24; - repeated AnotherMessage some_repeated_message = 25; -} - message ContainerMessage { extend ExtendableMessage { optional bool another_thing = 100; } } +// For testNanoOptionalGroupWithUnknownFieldsEnabled; +// not part of the extensions tests. message MessageWithGroup { optional group Group = 1 { optional int32 a = 2; diff --git a/src/google/protobuf/unittest_extension_packed_nano.proto b/src/google/protobuf/unittest_extension_packed_nano.proto new file mode 100644 index 0000000000..3586de76f1 --- /dev/null +++ b/src/google/protobuf/unittest_extension_packed_nano.proto @@ -0,0 +1,29 @@ +syntax = "proto2"; + +option java_multiple_files = true; +option java_package = "com.google.protobuf.nano"; + +import "google/protobuf/unittest_extension_nano.proto"; + +// Must be compiled separately due to extension number reuse. +// The reuse is deliberate, for testing wire compatibility. + +message PackedExtensions { + extend ExtendableMessage { + repeated int32 packed_int32 = 10 [ packed = true ]; + repeated uint32 packed_uint32 = 11 [ packed = true ]; + repeated sint32 packed_sint32 = 12 [ packed = true ]; + repeated int64 packed_int64 = 13 [ packed = true ]; + repeated uint64 packed_uint64 = 14 [ packed = true ]; + repeated sint64 packed_sint64 = 15 [ packed = true ]; + repeated fixed32 packed_fixed32 = 16 [ packed = true ]; + repeated sfixed32 packed_sfixed32 = 17 [ packed = true ]; + repeated fixed64 packed_fixed64 = 18 [ packed = true ]; + repeated sfixed64 packed_sfixed64 = 19 [ packed = true ]; + repeated bool packed_bool = 20 [ packed = true ]; + repeated float packed_float = 21 [ packed = true ]; + repeated double packed_double = 22 [ packed = true ]; + repeated AnEnum packed_enum = 23 [ packed = true ]; + // Non-packable types omitted. + } +} diff --git a/src/google/protobuf/unittest_extension_repeated_nano.proto b/src/google/protobuf/unittest_extension_repeated_nano.proto new file mode 100644 index 0000000000..546c2df1d7 --- /dev/null +++ b/src/google/protobuf/unittest_extension_repeated_nano.proto @@ -0,0 +1,34 @@ +syntax = "proto2"; + +option java_multiple_files = true; +option java_package = "com.google.protobuf.nano"; + +import "google/protobuf/unittest_extension_nano.proto"; + +// Must be compiled separately due to extension number reuse. +// The reuse is deliberate, for testing wire compatibility. + +message RepeatedExtensions { + extend ExtendableMessage { + repeated int32 repeated_int32 = 10; + repeated uint32 repeated_uint32 = 11; + repeated sint32 repeated_sint32 = 12; + repeated int64 repeated_int64 = 13; + repeated uint64 repeated_uint64 = 14; + repeated sint64 repeated_sint64 = 15; + repeated fixed32 repeated_fixed32 = 16; + repeated sfixed32 repeated_sfixed32 = 17; + repeated fixed64 repeated_fixed64 = 18; + repeated sfixed64 repeated_sfixed64 = 19; + repeated bool repeated_bool = 20; + repeated float repeated_float = 21; + repeated double repeated_double = 22; + repeated AnEnum repeated_enum = 23; + repeated string repeated_string = 24; + repeated bytes repeated_bytes = 25; + repeated AnotherMessage repeated_message = 26; + repeated group RepeatedGroup = 27 { + optional int32 a = 1; + } + } +} diff --git a/src/google/protobuf/unittest_extension_singular_nano.proto b/src/google/protobuf/unittest_extension_singular_nano.proto new file mode 100644 index 0000000000..35d9e6e99f --- /dev/null +++ b/src/google/protobuf/unittest_extension_singular_nano.proto @@ -0,0 +1,34 @@ +syntax = "proto2"; + +option java_multiple_files = true; +option java_package = "com.google.protobuf.nano"; + +import "google/protobuf/unittest_extension_nano.proto"; + +// Must be compiled separately due to extension number reuse. +// The reuse is deliberate, for testing wire compatibility. + +message SingularExtensions { + extend ExtendableMessage { + optional int32 some_int32 = 10; + optional uint32 some_uint32 = 11; + optional sint32 some_sint32 = 12; + optional int64 some_int64 = 13; + optional uint64 some_uint64 = 14; + optional sint64 some_sint64 = 15; + optional fixed32 some_fixed32 = 16; + optional sfixed32 some_sfixed32 = 17; + optional fixed64 some_fixed64 = 18; + optional sfixed64 some_sfixed64 = 19; + optional bool some_bool = 20; + optional float some_float = 21; + optional double some_double = 22; + optional AnEnum some_enum = 23; + optional string some_string = 24; + optional bytes some_bytes = 25; + optional AnotherMessage some_message = 26; + optional group SomeGroup = 27 { + optional int32 a = 1; + } + } +}