Extension overhaul.

- Get rid of TypeLiteral<T>. It was introduced to read the component
  type of a List<T> at runtime. But we use arrays everywhere else,
  and we can always read the component type of an array type at
  runtime.
- Properly read/write "minor" types (e.g. sint32, sfixed32). The old
  implementation could only read/write data as the "typical" types
  (one per Java type), e.g. java.lang.Integer -> int32, java.lang.Long
  -> int64. So if e.g. an extension specifies sfixed32 as the type, it
  would be read/written in the totally incompatible int32 format.
- Properly serialize repeated packed fields. The old implementation
  doesn't do packed serialization. As an added bonus, and to be more
  aligned with the rest of protobuf nano / main, repeated packable
  extensions can deserialize both packed and non-packed data.
- Split Extension class into a hierarchy so under typical usage a
  large chunk of code dealing with primitive type extensions can be
  removed by ProGuard.

Bug: https://code.google.com/p/android/issues/detail?id=62586
Change-Id: I0d692f35cc2a8ad3a5a1cb3ce001282b2356b041
pull/91/head
Max Cai 11 years ago
parent 0c0f8c7ef0
commit e3714f0001
  1. 31
      java/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java
  2. 64
      java/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java
  3. 700
      java/src/main/java/com/google/protobuf/nano/Extension.java
  4. 15
      java/src/main/java/com/google/protobuf/nano/MessageNano.java
  5. 236
      java/src/main/java/com/google/protobuf/nano/WireFormatNano.java
  6. 227
      java/src/test/java/com/google/protobuf/NanoTest.java
  7. 113
      src/google/protobuf/compiler/javanano/javanano_extension.cc
  8. 38
      src/google/protobuf/compiler/javanano/javanano_helpers.cc
  9. 8
      src/google/protobuf/compiler/javanano/javanano_helpers.h
  10. 92
      src/google/protobuf/compiler/javanano/javanano_message.cc
  11. 20
      src/google/protobuf/compiler/javanano/javanano_primitive_field.cc
  12. 21
      src/google/protobuf/unittest_extension_nano.proto
  13. 29
      src/google/protobuf/unittest_extension_packed_nano.proto
  14. 34
      src/google/protobuf/unittest_extension_repeated_nano.proto
  15. 34
      src/google/protobuf/unittest_extension_singular_nano.proto

@ -168,14 +168,6 @@ public final class CodedOutputByteBufferNano {
writeBytesNoTag(value); 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. */ /** Write a {@code uint32} field, including tag, to the stream. */
public void writeUInt32(final int fieldNumber, final int value) public void writeUInt32(final int fieldNumber, final int value)
throws IOException { throws IOException {
@ -321,12 +313,6 @@ public final class CodedOutputByteBufferNano {
writeRawBytes(value); 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. */ /** Write a {@code uint32} field to the stream. */
public void writeUInt32NoTag(final int value) throws IOException { public void writeUInt32NoTag(final int value) throws IOException {
writeRawVarint32(value); writeRawVarint32(value);
@ -466,15 +452,6 @@ public final class CodedOutputByteBufferNano {
return computeTagSize(fieldNumber) + computeBytesSizeNoTag(value); 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 * Compute the number of bytes that would be needed to encode a
* {@code uint32} field, including tag. * {@code uint32} field, including tag.
@ -660,14 +637,6 @@ public final class CodedOutputByteBufferNano {
return computeRawVarint32Size(value.length) + value.length; 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 * Compute the number of bytes that would be needed to encode a
* {@code uint32} field. * {@code uint32} field.

@ -30,6 +30,7 @@
package com.google.protobuf.nano; package com.google.protobuf.nano;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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, * Base class of those Protocol Buffer messages that need to store unknown fields,
* such as extensions. * such as extensions.
*/ */
public abstract class ExtendableMessageNano extends MessageNano { public abstract class ExtendableMessageNano<M extends ExtendableMessageNano<M>>
extends MessageNano {
/** /**
* A container for fields unknown to the message, including extensions. Extension fields can * 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> unknownFieldData; protected List<UnknownFieldData> unknownFieldData;
@Override @Override
public int getSerializedSize() { 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; cachedSize = size;
return 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. * Gets the value stored in the specified extension of this message.
*/ */
public <T> T getExtension(Extension<T> extension) { public final <T> T getExtension(Extension<M, T> extension) {
return WireFormatNano.getExtension(extension, unknownFieldData); return extension.getValueFrom(unknownFieldData);
} }
/** /**
* Sets the value of the specified extension of this message. * Sets the value of the specified extension of this message.
*/ */
public <T> void setExtension(Extension<T> extension, T value) { public final <T> M setExtension(Extension<M, T> 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.
*
* <p>Generated messages will call this for unknown fields if the store_unknown_fields
* option is on.
*
* <p>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) { if (unknownFieldData == null) {
unknownFieldData = new ArrayList<UnknownFieldData>(); unknownFieldData = new ArrayList<UnknownFieldData>();
} }
WireFormatNano.setExtension(extension, value, unknownFieldData); int endPos = input.getPosition();
byte[] bytes = input.getData(startPos, endPos - startPos);
unknownFieldData.add(new UnknownFieldData(tag, bytes));
return true;
} }
} }

@ -30,85 +30,659 @@
package com.google.protobuf.nano; package com.google.protobuf.nano;
import java.lang.reflect.ParameterizedType; import java.io.IOException;
import java.lang.reflect.Type; import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* Represents an extension. * Represents an extension.
* *
* @author bduff@google.com (Brian Duff) * @author bduff@google.com (Brian Duff)
* @param <T> the type of the extension. * @author maxtroy@google.com (Max Cai)
* @param <M> the type of the extendable message this extension is for.
* @param <T> the Java type of the extension; see {@link #clazz}.
*/ */
public class Extension<T> { public class Extension<M extends ExtendableMessageNano<M>, T> {
public final int fieldNumber;
public boolean isRepeatedField; /*
public Class<T> fieldType; * Because we typically only define message-typed extensions, the Extension class hierarchy is
public Class<T> listType; * designed as follows, to allow a big amount of code in this file to be removed by ProGuard:
*
private Extension(int fieldNumber, TypeLiteral<T> type) { * Extension // ready to use for message/group typed extensions
this.fieldNumber = fieldNumber; * Δ
isRepeatedField = type.isList(); * |
fieldType = type.getTargetClass(); * PrimitiveExtension // for primitive/enum typed extensions
listType = isRepeatedField ? type.getListType() : null; */
}
public static final int TYPE_DOUBLE = 1;
/** public static final int TYPE_FLOAT = 2;
* Creates a new instance of {@code Extension} for the specified {@code fieldNumber} and public static final int TYPE_INT64 = 3;
* {@code type}. public static final int TYPE_UINT64 = 4;
*/ public static final int TYPE_INT32 = 5;
public static <T> Extension<T> create(int fieldNumber, TypeLiteral<T> type) { public static final int TYPE_FIXED64 = 6;
return new Extension<T>(fieldNumber, type); 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;
* Creates a new instance of {@code Extension} for the specified {@code fieldNumber} and public static final int TYPE_MESSAGE = 11;
* {@code type}. This version is used for repeated fields. public static final int TYPE_BYTES = 12;
*/ public static final int TYPE_UINT32 = 13;
public static <T> Extension<List<T>> createRepeated(int fieldNumber, TypeLiteral<List<T>> type) { public static final int TYPE_ENUM = 14;
return new Extension<List<T>>(fieldNumber, type); 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;
* Represents a generic type literal. We can't typesafely reference a
* Class&lt;List&lt;Foo>>.class in Java, so we use this instead. /**
* See: http://gafter.blogspot.com/2006/12/super-type-tokens.html * Creates an {@code Extension} of the given message type and tag number.
* * Should be used by the generated code only.
* <p>Somewhat specialized because we only ever have a Foo or a List&lt;Foo>. *
*/ * @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP}
public static abstract class TypeLiteral<T> { */
private final Type type; public static <M extends ExtendableMessageNano<M>, T extends MessageNano>
Extension<M, T> createMessageTyped(int type, Class<T> clazz, int tag) {
protected TypeLiteral() { return new Extension<M, T>(type, clazz, tag, false);
Type superclass = getClass().getGenericSuperclass(); }
if (superclass instanceof Class) {
throw new RuntimeException("Missing type parameter"); /**
} * Creates a repeated {@code Extension} of the given message type and tag number.
this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0]; * Should be used by the generated code only.
*
* @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP}
*/
public static <M extends ExtendableMessageNano<M>, T extends MessageNano>
Extension<M, T[]> createRepeatedMessageTyped(int type, Class<T[]> clazz, int tag) {
return new Extension<M, T[]>(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 <M extends ExtendableMessageNano<M>, T>
Extension<M, T> createPrimitiveTyped(int type, Class<T> clazz, int tag) {
return new PrimitiveExtension<M, T>(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 <M extends ExtendableMessageNano<M>, T>
Extension<M, T> createRepeatedPrimitiveTyped(
int type, Class<T> clazz, int tag, int nonPackedTag, int packedTag) {
return new PrimitiveExtension<M, T>(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<T> clazz;
/**
* Tag number of this extension.
*/
protected final int tag;
/**
* Whether this extension is repeated.
*/
protected final boolean repeated;
private Extension(int type, Class<T> 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<UnknownFieldData> unknownFields) {
if (unknownFields == null) {
return null;
}
if (repeated) {
// For repeated extensions, read all matching unknown fields in their original order.
List<Object> resultList = new ArrayList<Object>();
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<Object> 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() { final List<UnknownFieldData> setValueTo(T value, List<UnknownFieldData> unknownFields) {
return type instanceof ParameterizedType; 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<UnknownFieldData>();
}
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") protected UnknownFieldData writeData(Object value) {
private Class<T> getListType() { // This implementation is for message/group extensions.
return (Class<T>) ((ParameterizedType) type).getRawType(); 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<UnknownFieldData> 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, * Represents an extension of a primitive (including enum) type. If there is no primitive
* returns the actual type. * extensions, this subclass will be removable by ProGuard.
*/ */
@SuppressWarnings("unchecked") private static class PrimitiveExtension<M extends ExtendableMessageNano<M>, T>
private Class<T> getTargetClass() { extends Extension<M, T> {
if (isList()) {
return (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[0]; /**
} * Tag of a piece of non-packed data from the wire compatible with this extension.
return (Class<T>) type; */
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<T> 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<Object> 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<UnknownFieldData> 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);
}
}
} }
}
} }

@ -67,16 +67,20 @@ public abstract class MessageNano {
} }
/** /**
* Serializes the message and writes it to {@code output}. This does not * Serializes the message and writes it to {@code output}.
* flush or close the stream. *
* @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 * Parse {@code input} as a message of this type and merge it with the
* message being built. * 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. * Serialize to a byte array.
@ -95,9 +99,8 @@ public abstract class MessageNano {
* write more than length bytes OutOfSpaceException will be thrown * write more than length bytes OutOfSpaceException will be thrown
* and if length bytes are not written then IllegalStateException * and if length bytes are not written then IllegalStateException
* is thrown. * 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 { try {
final CodedOutputByteBufferNano output = final CodedOutputByteBufferNano output =
CodedOutputByteBufferNano.newInstance(data, offset, length); CodedOutputByteBufferNano.newInstance(data, offset, length);

@ -31,9 +31,6 @@
package com.google.protobuf.nano; package com.google.protobuf.nano;
import java.io.IOException; 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 * 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; 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 int EMPTY_INT_ARRAY[] = {};
public static final long EMPTY_LONG_ARRAY[] = {}; public static final long EMPTY_LONG_ARRAY[] = {};
public static final float EMPTY_FLOAT_ARRAY[] = {}; public static final float EMPTY_FLOAT_ARRAY[] = {};
@ -113,35 +95,6 @@ public final class WireFormatNano {
return input.skipField(tag); return input.skipField(tag);
} }
/**
* Stores the binary data of an unknown field.
*
* <p>Generated messages will call this for unknown fields if the store_unknown_fields
* option is on.
*
* <p>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<UnknownFieldData> 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 * 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 * fields are contiguously serialized but we still correctly handle interspersed values of a
@ -172,193 +125,4 @@ public final class WireFormatNano {
return arrayLength; return arrayLength;
} }
/**
* Decodes the value of an extension.
*/
public static <T> T getExtension(Extension<T> extension, List<UnknownFieldData> unknownFields) {
if (unknownFields == null) {
return null;
}
List<UnknownFieldData> dataForField = new ArrayList<UnknownFieldData>();
for (UnknownFieldData data : unknownFields) {
if (getTagFieldNumber(data.tag) == extension.fieldNumber) {
dataForField.add(data);
}
}
if (dataForField.isEmpty()) {
return null;
}
if (extension.isRepeatedField) {
List<Object> result = new ArrayList<Object>(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> T readData(Class<T> 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 <T> void setExtension(Extension<T> extension, T value,
List<UnknownFieldData> unknownFields) {
// First, remove all unknown fields with this tag.
for (Iterator<UnknownFieldData> 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<UnknownFieldData> 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<UnknownFieldData> 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);
}
}
} }

@ -50,6 +50,9 @@ import com.google.protobuf.nano.NanoOuterClass;
import com.google.protobuf.nano.NanoOuterClass.TestAllTypesNano; import com.google.protobuf.nano.NanoOuterClass.TestAllTypesNano;
import com.google.protobuf.nano.NanoReferenceTypes; import com.google.protobuf.nano.NanoReferenceTypes;
import com.google.protobuf.nano.NanoRepeatedPackables; 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.TestRepeatedMergeNano;
import com.google.protobuf.nano.UnittestImportNano; import com.google.protobuf.nano.UnittestImportNano;
import com.google.protobuf.nano.UnittestMultipleNano; import com.google.protobuf.nano.UnittestMultipleNano;
@ -59,10 +62,8 @@ import com.google.protobuf.nano.UnittestSingleNano.SingleMessageNano;
import junit.framework.TestCase; import junit.framework.TestCase;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
/** /**
* Test nano runtime. * Test nano runtime.
@ -518,12 +519,12 @@ public class NanoTest extends TestCase {
byte [] serialized = MessageNano.toByteArray(msg); byte [] serialized = MessageNano.toByteArray(msg);
MessageWithGroup parsed = MessageWithGroup.parseFrom(serialized); MessageWithGroup parsed = MessageWithGroup.parseFrom(serialized);
assertTrue(msg.group != null); assertEquals(1, parsed.group.a);
assertEquals(1, msg.group.a);
byte [] serialized2 = MessageNano.toByteArray(parsed); byte [] serialized2 = MessageNano.toByteArray(parsed);
assertEquals(serialized2.length, serialized.length); assertEquals(serialized.length, serialized2.length);
MessageWithGroup parsed2 = MessageWithGroup.parseFrom(serialized2); MessageWithGroup parsed2 = MessageWithGroup.parseFrom(serialized2);
assertEquals(1, parsed2.group.a);
} }
public void testNanoOptionalNestedMessage() throws Exception { public void testNanoOptionalNestedMessage() throws Exception {
@ -2100,6 +2101,7 @@ public class NanoTest extends TestCase {
*/ */
public void testNanoSingle() throws Exception { public void testNanoSingle() throws Exception {
SingleMessageNano msg = new SingleMessageNano(); SingleMessageNano msg = new SingleMessageNano();
assertNotNull(msg);
} }
/** /**
@ -2470,12 +2472,14 @@ public class NanoTest extends TestCase {
msg.defaultFloatNan = 0; msg.defaultFloatNan = 0;
byte[] result = MessageNano.toByteArray(msg); byte[] result = MessageNano.toByteArray(msg);
int msgSerializedSize = msg.getSerializedSize(); int msgSerializedSize = msg.getSerializedSize();
assertTrue(result.length == msgSerializedSize);
assertTrue(msgSerializedSize > 3); assertTrue(msgSerializedSize > 3);
msg.defaultDoubleNan = Double.NaN; msg.defaultDoubleNan = Double.NaN;
msg.defaultFloatNan = Float.NaN; msg.defaultFloatNan = Float.NaN;
result = MessageNano.toByteArray(msg); result = MessageNano.toByteArray(msg);
msgSerializedSize = msg.getSerializedSize(); msgSerializedSize = msg.getSerializedSize();
assertEquals(3, result.length);
assertEquals(3, msgSerializedSize); assertEquals(3, msgSerializedSize);
} }
@ -2608,57 +2612,163 @@ public class NanoTest extends TestCase {
public void testExtensions() throws Exception { public void testExtensions() throws Exception {
Extensions.ExtendableMessage message = new Extensions.ExtendableMessage(); Extensions.ExtendableMessage message = new Extensions.ExtendableMessage();
message.field = 5; message.field = 5;
message.setExtension(Extensions.someString, "Hello World!"); int[] int32s = {1, 2};
message.setExtension(Extensions.someBool, true); int[] uint32s = {3, 4};
message.setExtension(Extensions.someInt, 42); int[] sint32s = {-5, -6};
message.setExtension(Extensions.someLong, 124234234234L); long[] int64s = {7, 8};
message.setExtension(Extensions.someFloat, 42.0f); long[] uint64s = {9, 10};
message.setExtension(Extensions.someDouble, 422222.0); long[] sint64s = {-11, -12};
message.setExtension(Extensions.someEnum, Extensions.FIRST_VALUE); int[] fixed32s = {13, 14};
AnotherMessage another = new AnotherMessage(); int[] sfixed32s = {-15, -16};
another.string = "Foo"; long[] fixed64s = {17, 18};
another.value = true; long[] sfixed64s = {-19, -20};
message.setExtension(Extensions.someMessage, another); boolean[] bools = {true, false};
float[] floats = {2.1f, 2.2f};
message.setExtension(Extensions.someRepeatedString, list("a", "bee", "seeya")); double[] doubles = {2.3, 2.4};
message.setExtension(Extensions.someRepeatedBool, list(true, false, true)); int[] enums = {Extensions.SECOND_VALUE, Extensions.FIRST_VALUE};
message.setExtension(Extensions.someRepeatedInt, list(4, 8, 15, 16, 23, 42)); String[] strings = {"vijfentwintig", "twenty-six"};
message.setExtension(Extensions.someRepeatedLong, list(4L, 8L, 15L, 16L, 23L, 42L)); byte[][] bytess = {{2, 7}, {2, 8}};
message.setExtension(Extensions.someRepeatedFloat, list(1.0f, 3.0f)); AnotherMessage another1 = new AnotherMessage();
message.setExtension(Extensions.someRepeatedDouble, list(55.133, 3.14159)); another1.string = "er shi jiu";
message.setExtension(Extensions.someRepeatedEnum, list(Extensions.FIRST_VALUE, another1.value = false;
Extensions.SECOND_VALUE)); AnotherMessage another2 = new AnotherMessage();
AnotherMessage second = new AnotherMessage(); another2.string = "trente";
second.string = "Whee"; another2.value = true;
second.value = false; AnotherMessage[] messages = {another1, another2};
message.setExtension(Extensions.someRepeatedMessage, list(another, second)); 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); byte[] data = MessageNano.toByteArray(message);
message = Extensions.ExtendableMessage.parseFrom(data);
Extensions.ExtendableMessage deserialized = Extensions.ExtendableMessage.parseFrom(data); assertEquals(5, message.field);
assertEquals(5, deserialized.field);
assertEquals("Hello World!", deserialized.getExtension(Extensions.someString)); // Test reading back using SingularExtensions: the retrieved value should equal the last
assertEquals(Boolean.TRUE, deserialized.getExtension(Extensions.someBool)); // in each array.
assertEquals(Integer.valueOf(42), deserialized.getExtension(Extensions.someInt)); assertEquals(int32s[1], (int) message.getExtension(SingularExtensions.someInt32));
assertEquals(Long.valueOf(124234234234L), deserialized.getExtension(Extensions.someLong)); assertEquals(uint32s[1], (int) message.getExtension(SingularExtensions.someUint32));
assertEquals(Float.valueOf(42.0f), deserialized.getExtension(Extensions.someFloat)); assertEquals(sint32s[1], (int) message.getExtension(SingularExtensions.someSint32));
assertEquals(Double.valueOf(422222.0), deserialized.getExtension(Extensions.someDouble)); assertEquals(int64s[1], (long) message.getExtension(SingularExtensions.someInt64));
assertEquals(Integer.valueOf(Extensions.FIRST_VALUE), assertEquals(uint64s[1], (long) message.getExtension(SingularExtensions.someUint64));
deserialized.getExtension(Extensions.someEnum)); assertEquals(sint64s[1], (long) message.getExtension(SingularExtensions.someSint64));
assertEquals(another.string, deserialized.getExtension(Extensions.someMessage).string); assertEquals(fixed32s[1], (int) message.getExtension(SingularExtensions.someFixed32));
assertEquals(another.value, deserialized.getExtension(Extensions.someMessage).value); assertEquals(sfixed32s[1], (int) message.getExtension(SingularExtensions.someSfixed32));
assertEquals(list("a", "bee", "seeya"), deserialized.getExtension(Extensions.someRepeatedString)); assertEquals(fixed64s[1], (long) message.getExtension(SingularExtensions.someFixed64));
assertEquals(list(true, false, true), deserialized.getExtension(Extensions.someRepeatedBool)); assertEquals(sfixed64s[1], (long) message.getExtension(SingularExtensions.someSfixed64));
assertEquals(list(4, 8, 15, 16, 23, 42), deserialized.getExtension(Extensions.someRepeatedInt)); assertEquals(bools[1], (boolean) message.getExtension(SingularExtensions.someBool));
assertEquals(list(4L, 8L, 15L, 16L, 23L, 42L), deserialized.getExtension(Extensions.someRepeatedLong)); assertEquals(floats[1], (float) message.getExtension(SingularExtensions.someFloat));
assertEquals(list(1.0f, 3.0f), deserialized.getExtension(Extensions.someRepeatedFloat)); assertEquals(doubles[1], (double) message.getExtension(SingularExtensions.someDouble));
assertEquals(list(55.133, 3.14159), deserialized.getExtension(Extensions.someRepeatedDouble)); assertEquals(enums[1], (int) message.getExtension(SingularExtensions.someEnum));
assertEquals(list(Extensions.FIRST_VALUE, assertEquals(strings[1], message.getExtension(SingularExtensions.someString));
Extensions.SECOND_VALUE), deserialized.getExtension(Extensions.someRepeatedEnum)); assertTrue(Arrays.equals(bytess[1], message.getExtension(SingularExtensions.someBytes)));
assertEquals("Foo", deserialized.getExtension(Extensions.someRepeatedMessage).get(0).string); AnotherMessage deserializedMessage = message.getExtension(SingularExtensions.someMessage);
assertEquals(true, deserialized.getExtension(Extensions.someRepeatedMessage).get(0).value); assertEquals(another2.string, deserializedMessage.string);
assertEquals("Whee", deserialized.getExtension(Extensions.someRepeatedMessage).get(1).string); assertEquals(another2.value, deserializedMessage.value);
assertEquals(false, deserialized.getExtension(Extensions.someRepeatedMessage).get(1).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 { public void testUnknownFields() throws Exception {
@ -3220,13 +3330,4 @@ public class NanoTest extends TestCase {
} }
return sb.toString(); return sb.toString();
} }
private <T> List<T> list(T first, T... remaining) {
List<T> list = new ArrayList<T>();
list.add(first);
for (T item : remaining) {
list.add(item);
}
return list;
}
} }

@ -42,28 +42,84 @@ namespace compiler {
namespace javanano { namespace javanano {
using internal::WireFormat; 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, void SetVariables(const FieldDescriptor* descriptor, const Params params,
map<string, string>* variables) { map<string, string>* variables) {
(*variables)["name"] =
RenameJavaKeywords(UnderscoresToCamelCase(descriptor));
(*variables)["number"] = SimpleItoa(descriptor->number());
(*variables)["extends"] = ClassName(params, descriptor->containing_type()); (*variables)["extends"] = ClassName(params, descriptor->containing_type());
(*variables)["name"] = RenameJavaKeywords(UnderscoresToCamelCase(descriptor));
string type; bool repeated = descriptor->is_repeated();
(*variables)["repeated"] = repeated ? "Repeated" : "";
(*variables)["type"] = GetTypeConstantName(descriptor->type());
JavaType java_type = GetJavaType(descriptor->type()); JavaType java_type = GetJavaType(descriptor->type());
switch (java_type) { string tag = SimpleItoa(WireFormat::MakeTag(descriptor));
case JAVATYPE_ENUM: if (java_type == JAVATYPE_MESSAGE) {
type = "java.lang.Integer"; (*variables)["ext_type"] = "MessageTyped";
break; string message_type = ClassName(params, descriptor->message_type());
case JAVATYPE_MESSAGE: if (repeated) {
type = ClassName(params, descriptor->message_type()); message_type += "[]";
break; }
default: (*variables)["class"] = message_type;
type = BoxedPrimitiveTypeName(java_type); // For message typed extensions, tags_params contains a single tag
break; // 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:: ExtensionGenerator::
@ -75,21 +131,16 @@ ExtensionGenerator(const FieldDescriptor* descriptor, const Params& params)
ExtensionGenerator::~ExtensionGenerator() {} ExtensionGenerator::~ExtensionGenerator() {}
void ExtensionGenerator::Generate(io::Printer* printer) const { void ExtensionGenerator::Generate(io::Printer* printer) const {
if (descriptor_->is_repeated()) { printer->Print("\n");
printer->Print(variables_, PrintFieldComment(printer, descriptor_);
"\n" printer->Print(variables_,
"// extends $extends$\n" "public static final com.google.protobuf.nano.Extension<\n"
"public static final com.google.protobuf.nano.Extension<java.util.List<$type$>> $name$ = \n" " $extends$,\n"
" com.google.protobuf.nano.Extension.createRepeated($number$,\n" " $class$> $name$ =\n"
" new com.google.protobuf.nano.Extension.TypeLiteral<java.util.List<$type$>>(){});\n"); " com.google.protobuf.nano.Extension.create$repeated$$ext_type$(\n"
} else { " com.google.protobuf.nano.Extension.$type$,\n"
printer->Print(variables_, " $class$.class,\n"
"\n" " $tag_params$);\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");
}
} }
} // namespace javanano } // namespace javanano

@ -264,6 +264,22 @@ string FieldDefaultConstantName(const FieldDescriptor *field) {
return "_" + RenameJavaKeywords(UnderscoresToCamelCase(field)) + "Default"; 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) { JavaType GetJavaType(FieldDescriptor::Type field_type) {
switch (field_type) { switch (field_type) {
case FieldDescriptor::TYPE_INT32: case FieldDescriptor::TYPE_INT32:
@ -310,7 +326,27 @@ JavaType GetJavaType(FieldDescriptor::Type field_type) {
return JAVATYPE_INT; 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) { switch (type) {
case JAVATYPE_INT : return "java.lang.Integer"; case JAVATYPE_INT : return "java.lang.Integer";
case JAVATYPE_LONG : return "java.lang.Long"; case JAVATYPE_LONG : return "java.lang.Long";

@ -39,6 +39,7 @@
#include <google/protobuf/compiler/javanano/javanano_params.h> #include <google/protobuf/compiler/javanano/javanano_params.h>
#include <google/protobuf/descriptor.pb.h> #include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/descriptor.h> #include <google/protobuf/descriptor.h>
#include <google/protobuf/io/printer.h>
namespace google { namespace google {
namespace protobuf { namespace protobuf {
@ -111,6 +112,9 @@ string FieldConstantName(const FieldDescriptor *field);
string FieldDefaultConstantName(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 { enum JavaType {
JAVATYPE_INT, JAVATYPE_INT,
JAVATYPE_LONG, JAVATYPE_LONG,
@ -129,10 +133,12 @@ inline JavaType GetJavaType(const FieldDescriptor* field) {
return GetJavaType(field->type()); return GetJavaType(field->type());
} }
string PrimitiveTypeName(JavaType type);
// Get the fully-qualified class name for a boxed primitive type, e.g. // 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 // "java.lang.Integer" for JAVATYPE_INT. Returns NULL for enum and message
// types. // types.
const char* BoxedPrimitiveTypeName(JavaType type); string BoxedPrimitiveTypeName(JavaType type);
string EmptyArrayName(const Params& params, const FieldDescriptor* field); string EmptyArrayName(const Params& params, const FieldDescriptor* field);

@ -54,14 +54,6 @@ using internal::WireFormatLite;
namespace { 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 { struct FieldOrderingByNumber {
inline bool operator()(const FieldDescriptor* a, inline bool operator()(const FieldDescriptor* a,
const FieldDescriptor* b) const { const FieldDescriptor* b) const {
@ -82,13 +74,6 @@ const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor) {
return fields; 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 } // namespace
// =================================================================== // ===================================================================
@ -149,7 +134,8 @@ void MessageGenerator::Generate(io::Printer* printer) {
} }
if (params_.store_unknown_fields()) { if (params_.store_unknown_fields()) {
printer->Print( printer->Print(
" com.google.protobuf.nano.ExtendableMessageNano {\n"); " com.google.protobuf.nano.ExtendableMessageNano<$classname$> {\n",
"classname", descriptor_->name());
} else { } else {
printer->Print( printer->Print(
" com.google.protobuf.nano.MessageNano {\n"); " com.google.protobuf.nano.MessageNano {\n");
@ -285,22 +271,20 @@ void MessageGenerator::Generate(io::Printer* printer) {
void MessageGenerator:: void MessageGenerator::
GenerateMessageSerializationMethods(io::Printer* printer) { 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<const FieldDescriptor*> sorted_fields( scoped_array<const FieldDescriptor*> sorted_fields(
SortFieldsByNumber(descriptor_)); SortFieldsByNumber(descriptor_));
// writeTo only throws an exception if it contains one or more fields to write printer->Print(
if (descriptor_->field_count() > 0 || params_.store_unknown_fields()) { "\n"
printer->Print( "@Override\n"
"\n" "public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output)\n"
"@Override\n" " throws java.io.IOException {\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->Indent(); printer->Indent();
// Output the fields in sorted order // Output the fields in sorted order
@ -308,36 +292,31 @@ GenerateMessageSerializationMethods(io::Printer* printer) {
GenerateSerializeOneField(printer, sorted_fields[i]); GenerateSerializeOneField(printer, sorted_fields[i]);
} }
// Write unknown fields. // The parent implementation will write any unknown fields if necessary.
if (params_.store_unknown_fields()) { printer->Print(
printer->Print( "super.writeTo(output);\n");
"com.google.protobuf.nano.WireFormatNano.writeUnknownFields(\n"
" unknownFieldData, output);\n");
}
printer->Outdent(); printer->Outdent();
printer->Print("}\n"); printer->Print("}\n");
// Rely on the parent implementation of getSerializedSize if there are no fields to // The parent implementation will get the serialized size for unknown
// serialize in this MessageNano. // fields if necessary.
if (descriptor_->field_count() != 0) { printer->Print(
printer->Print( "\n"
"\n" "@Override\n"
"@Override\n" "public int getSerializedSize() {\n"
"public int getSerializedSize() {\n" " int size = super.getSerializedSize();\n");
" int size = super.getSerializedSize();\n"); printer->Indent();
printer->Indent();
for (int i = 0; i < descriptor_->field_count(); i++) {
field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer);
}
printer->Outdent(); for (int i = 0; i < descriptor_->field_count(); i++) {
printer->Print( field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer);
" cachedSize = size;\n"
" return size;\n"
"}\n");
} }
printer->Outdent();
printer->Print(
" cachedSize = size;\n"
" return size;\n"
"}\n");
} }
void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) { void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) {
@ -371,12 +350,7 @@ void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) {
printer->Indent(); printer->Indent();
if (params_.store_unknown_fields()) { if (params_.store_unknown_fields()) {
printer->Print( printer->Print(
"if (unknownFieldData == null) {\n" "if (!storeUnknownField(input, tag)) {\n"
" unknownFieldData =\n"
" new java.util.ArrayList<com.google.protobuf.nano.UnknownFieldData>();\n"
"}\n"
"if (!com.google.protobuf.nano.WireFormatNano.storeUnknownField(\n"
" unknownFieldData, input, tag)) {\n"
" return this;\n" " return this;\n"
"}\n"); "}\n");
} else { } else {

@ -54,26 +54,6 @@ using internal::WireFormatLite;
namespace { 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) { bool IsReferenceType(JavaType type) {
switch (type) { switch (type) {
case JAVATYPE_INT : return false; case JAVATYPE_INT : return false;

@ -18,31 +18,14 @@ message AnotherMessage {
optional bool value = 2; 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 { message ContainerMessage {
extend ExtendableMessage { extend ExtendableMessage {
optional bool another_thing = 100; optional bool another_thing = 100;
} }
} }
// For testNanoOptionalGroupWithUnknownFieldsEnabled;
// not part of the extensions tests.
message MessageWithGroup { message MessageWithGroup {
optional group Group = 1 { optional group Group = 1 {
optional int32 a = 2; optional int32 a = 2;

@ -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.
}
}

@ -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;
}
}
}

@ -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;
}
}
}
Loading…
Cancel
Save