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