|
@@ -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 <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 final int fieldNumber;
|
|
|
- public boolean isRepeatedField;
|
|
|
- public Class<T> fieldType;
|
|
|
- public Class<T> listType;
|
|
|
-
|
|
|
- private Extension(int fieldNumber, TypeLiteral<T> 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 <T> Extension<T> create(int fieldNumber, TypeLiteral<T> type) {
|
|
|
- return new Extension<T>(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 <T> Extension<List<T>> createRepeated(int fieldNumber, TypeLiteral<List<T>> type) {
|
|
|
- return new Extension<List<T>>(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
|
|
|
- *
|
|
|
- * <p>Somewhat specialized because we only ever have a Foo or a List<Foo>.
|
|
|
- */
|
|
|
- public static abstract class TypeLiteral<T> {
|
|
|
- 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<M extends ExtendableMessageNano<M>, 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 <M extends ExtendableMessageNano<M>, T extends MessageNano>
|
|
|
+ Extension<M, T> createMessageTyped(int type, Class<T> clazz, int tag) {
|
|
|
+ return new Extension<M, T>(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 <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() {
|
|
|
- return type instanceof ParameterizedType;
|
|
|
+ final List<UnknownFieldData> setValueTo(T value, List<UnknownFieldData> 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<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")
|
|
|
- private Class<T> getListType() {
|
|
|
- return (Class<T>) ((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<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,
|
|
|
- * 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<T> getTargetClass() {
|
|
|
- if (isList()) {
|
|
|
- return (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[0];
|
|
|
- }
|
|
|
- return (Class<T>) type;
|
|
|
+ private static class PrimitiveExtension<M extends ExtendableMessageNano<M>, T>
|
|
|
+ extends Extension<M, T> {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 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<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);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
}
|