707 lines
32 KiB
Java
707 lines
32 KiB
Java
// Protocol Buffers - Google's data interchange format
|
|
// Copyright 2013 Google Inc. All rights reserved.
|
|
// https://developers.google.com/protocol-buffers/
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above
|
|
// copyright notice, this list of conditions and the following disclaimer
|
|
// in the documentation and/or other materials provided with the
|
|
// distribution.
|
|
// * Neither the name of Google Inc. nor the names of its
|
|
// contributors may be used to endorse or promote products derived from
|
|
// this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
package com.google.protobuf.nano;
|
|
|
|
import java.io.IOException;
|
|
import java.lang.reflect.Array;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Represents an extension.
|
|
*
|
|
* @author bduff@google.com (Brian Duff)
|
|
* @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<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 = InternalNano.TYPE_DOUBLE;
|
|
public static final int TYPE_FLOAT = InternalNano.TYPE_FLOAT;
|
|
public static final int TYPE_INT64 = InternalNano.TYPE_INT64;
|
|
public static final int TYPE_UINT64 = InternalNano.TYPE_UINT64;
|
|
public static final int TYPE_INT32 = InternalNano.TYPE_INT32;
|
|
public static final int TYPE_FIXED64 = InternalNano.TYPE_FIXED64;
|
|
public static final int TYPE_FIXED32 = InternalNano.TYPE_FIXED32;
|
|
public static final int TYPE_BOOL = InternalNano.TYPE_BOOL;
|
|
public static final int TYPE_STRING = InternalNano.TYPE_STRING;
|
|
public static final int TYPE_GROUP = InternalNano.TYPE_GROUP;
|
|
public static final int TYPE_MESSAGE = InternalNano.TYPE_MESSAGE;
|
|
public static final int TYPE_BYTES = InternalNano.TYPE_BYTES;
|
|
public static final int TYPE_UINT32 = InternalNano.TYPE_UINT32;
|
|
public static final int TYPE_ENUM = InternalNano.TYPE_ENUM;
|
|
public static final int TYPE_SFIXED32 = InternalNano.TYPE_SFIXED32;
|
|
public static final int TYPE_SFIXED64 = InternalNano.TYPE_SFIXED64;
|
|
public static final int TYPE_SINT32 = InternalNano.TYPE_SINT32;
|
|
public static final int TYPE_SINT64 = InternalNano.TYPE_SINT64;
|
|
|
|
/**
|
|
* 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}
|
|
* @deprecated use {@link #createMessageTyped(int, Class, long)} instead.
|
|
*/
|
|
@Deprecated
|
|
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);
|
|
}
|
|
|
|
// Note: these create...() methods take a long for the tag parameter,
|
|
// because tags are represented as unsigned ints, and these values exist
|
|
// in generated code as long values. However, they can fit in 32-bits, so
|
|
// it's safe to cast them to int without loss of precision.
|
|
|
|
/**
|
|
* 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, long tag) {
|
|
return new Extension<M, T>(type, clazz, (int) 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, long tag) {
|
|
return new Extension<M, T[]>(type, clazz, (int) 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, long tag) {
|
|
return new PrimitiveExtension<M, T>(type, clazz, (int) 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, long tag, long nonPackedTag, long packedTag) {
|
|
return new PrimitiveExtension<M, T>(type, clazz, (int) tag, true,
|
|
(int) nonPackedTag, (int) 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. The data should be viewed as an unsigned 32-bit value.
|
|
*/
|
|
public 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;
|
|
}
|
|
|
|
/**
|
|
* Returns the value of this extension stored in the given list of unknown fields, or
|
|
* {@code null} if no unknown fields matches this extension.
|
|
*
|
|
* @param unknownFields a list of {@link UnknownFieldData}. All of the elements must have a tag
|
|
* that matches this Extension's tag.
|
|
*
|
|
*/
|
|
final T getValueFrom(List<UnknownFieldData> unknownFields) {
|
|
if (unknownFields == null) {
|
|
return null;
|
|
}
|
|
return repeated ? getRepeatedValueFrom(unknownFields) : getSingularValueFrom(unknownFields);
|
|
}
|
|
|
|
private T getRepeatedValueFrom(List<UnknownFieldData> unknownFields) {
|
|
// 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 (data.bytes.length != 0) {
|
|
readDataInto(data, resultList);
|
|
}
|
|
}
|
|
|
|
int resultSize = resultList.size();
|
|
if (resultSize == 0) {
|
|
return null;
|
|
} else {
|
|
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;
|
|
}
|
|
}
|
|
|
|
private T getSingularValueFrom(List<UnknownFieldData> unknownFields) {
|
|
// For singular extensions, get the last piece of data stored under this extension.
|
|
if (unknownFields.isEmpty()) {
|
|
return null;
|
|
}
|
|
UnknownFieldData lastData = unknownFields.get(unknownFields.size() - 1);
|
|
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)));
|
|
}
|
|
|
|
void writeTo(Object value, CodedOutputByteBufferNano output) throws IOException {
|
|
if (repeated) {
|
|
writeRepeatedData(value, output);
|
|
} else {
|
|
writeSingularData(value, output);
|
|
}
|
|
}
|
|
|
|
protected void writeSingularData(Object value, CodedOutputByteBufferNano out) {
|
|
// This implementation is for message/group extensions.
|
|
try {
|
|
out.writeRawVarint32(tag);
|
|
switch (type) {
|
|
case TYPE_GROUP:
|
|
MessageNano groupValue = (MessageNano) value;
|
|
int fieldNumber = WireFormatNano.getTagFieldNumber(tag);
|
|
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;
|
|
out.writeMessageNoTag(messageValue);
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException("Unknown type " + type);
|
|
}
|
|
} catch (IOException e) {
|
|
// Should not happen
|
|
throw new IllegalStateException(e);
|
|
}
|
|
}
|
|
|
|
protected void writeRepeatedData(Object array, CodedOutputByteBufferNano output) {
|
|
// 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) {
|
|
writeSingularData(element, output);
|
|
}
|
|
}
|
|
}
|
|
|
|
int computeSerializedSize(Object value) {
|
|
if (repeated) {
|
|
return computeRepeatedSerializedSize(value);
|
|
} else {
|
|
return computeSingularSerializedSize(value);
|
|
}
|
|
}
|
|
|
|
protected int computeRepeatedSerializedSize(Object array) {
|
|
// This implementation is for non-packed extensions.
|
|
int size = 0;
|
|
int arrayLength = Array.getLength(array);
|
|
for (int i = 0; i < arrayLength; i++) {
|
|
Object element = Array.get(array, i);
|
|
if (element != null) {
|
|
size += computeSingularSerializedSize(Array.get(array, i));
|
|
}
|
|
}
|
|
return size;
|
|
}
|
|
|
|
protected int computeSingularSerializedSize(Object value) {
|
|
// This implementation is for message/group extensions.
|
|
int fieldNumber = WireFormatNano.getTagFieldNumber(tag);
|
|
switch (type) {
|
|
case TYPE_GROUP:
|
|
MessageNano groupValue = (MessageNano) value;
|
|
return CodedOutputByteBufferNano.computeGroupSize(fieldNumber, groupValue);
|
|
case TYPE_MESSAGE:
|
|
MessageNano messageValue = (MessageNano) value;
|
|
return CodedOutputByteBufferNano.computeMessageSize(fieldNumber, messageValue);
|
|
default:
|
|
throw new IllegalArgumentException("Unknown type " + type);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Represents an extension of a primitive (including enum) type. If there is no primitive
|
|
* extensions, this subclass will be removable by ProGuard.
|
|
*/
|
|
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 Object readData(CodedInputByteBufferNano input) {
|
|
try {
|
|
return input.readPrimitiveField(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 void writeSingularData(Object value, CodedOutputByteBufferNano output) {
|
|
try {
|
|
output.writeRawVarint32(tag);
|
|
switch (type) {
|
|
case TYPE_DOUBLE:
|
|
Double doubleValue = (Double) value;
|
|
output.writeDoubleNoTag(doubleValue);
|
|
break;
|
|
case TYPE_FLOAT:
|
|
Float floatValue = (Float) value;
|
|
output.writeFloatNoTag(floatValue);
|
|
break;
|
|
case TYPE_INT64:
|
|
Long int64Value = (Long) value;
|
|
output.writeInt64NoTag(int64Value);
|
|
break;
|
|
case TYPE_UINT64:
|
|
Long uint64Value = (Long) value;
|
|
output.writeUInt64NoTag(uint64Value);
|
|
break;
|
|
case TYPE_INT32:
|
|
Integer int32Value = (Integer) value;
|
|
output.writeInt32NoTag(int32Value);
|
|
break;
|
|
case TYPE_FIXED64:
|
|
Long fixed64Value = (Long) value;
|
|
output.writeFixed64NoTag(fixed64Value);
|
|
break;
|
|
case TYPE_FIXED32:
|
|
Integer fixed32Value = (Integer) value;
|
|
output.writeFixed32NoTag(fixed32Value);
|
|
break;
|
|
case TYPE_BOOL:
|
|
Boolean boolValue = (Boolean) value;
|
|
output.writeBoolNoTag(boolValue);
|
|
break;
|
|
case TYPE_STRING:
|
|
String stringValue = (String) value;
|
|
output.writeStringNoTag(stringValue);
|
|
break;
|
|
case TYPE_BYTES:
|
|
byte[] bytesValue = (byte[]) value;
|
|
output.writeBytesNoTag(bytesValue);
|
|
break;
|
|
case TYPE_UINT32:
|
|
Integer uint32Value = (Integer) value;
|
|
output.writeUInt32NoTag(uint32Value);
|
|
break;
|
|
case TYPE_ENUM:
|
|
Integer enumValue = (Integer) value;
|
|
output.writeEnumNoTag(enumValue);
|
|
break;
|
|
case TYPE_SFIXED32:
|
|
Integer sfixed32Value = (Integer) value;
|
|
output.writeSFixed32NoTag(sfixed32Value);
|
|
break;
|
|
case TYPE_SFIXED64:
|
|
Long sfixed64Value = (Long) value;
|
|
output.writeSFixed64NoTag(sfixed64Value);
|
|
break;
|
|
case TYPE_SINT32:
|
|
Integer sint32Value = (Integer) value;
|
|
output.writeSInt32NoTag(sint32Value);
|
|
break;
|
|
case TYPE_SINT64:
|
|
Long sint64Value = (Long) value;
|
|
output.writeSInt64NoTag(sint64Value);
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException("Unknown type " + type);
|
|
}
|
|
} catch (IOException e) {
|
|
// Should not happen
|
|
throw new IllegalStateException(e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void writeRepeatedData(Object array, CodedOutputByteBufferNano output) {
|
|
if (tag == nonPackedTag) {
|
|
// Use base implementation for non-packed data
|
|
super.writeRepeatedData(array, output);
|
|
} 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.
|
|
int arrayLength = Array.getLength(array);
|
|
int dataSize = computePackedDataSize(array);
|
|
|
|
try {
|
|
output.writeRawVarint32(tag);
|
|
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);
|
|
}
|
|
} else {
|
|
throw new IllegalArgumentException("Unexpected repeated extension tag " + tag
|
|
+ ", unequal to both non-packed variant " + nonPackedTag
|
|
+ " and packed variant " + packedTag);
|
|
}
|
|
}
|
|
|
|
private int computePackedDataSize(Object array) {
|
|
int dataSize = 0;
|
|
int arrayLength = Array.getLength(array);
|
|
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);
|
|
}
|
|
return dataSize;
|
|
}
|
|
|
|
@Override
|
|
protected int computeRepeatedSerializedSize(Object array) {
|
|
if (tag == nonPackedTag) {
|
|
// Use base implementation for non-packed data
|
|
return super.computeRepeatedSerializedSize(array);
|
|
} else if (tag == packedTag) {
|
|
// Packed.
|
|
int dataSize = computePackedDataSize(array);
|
|
int payloadSize =
|
|
dataSize + CodedOutputByteBufferNano.computeRawVarint32Size(dataSize);
|
|
return payloadSize + CodedOutputByteBufferNano.computeRawVarint32Size(tag);
|
|
} else {
|
|
throw new IllegalArgumentException("Unexpected repeated extension tag " + tag
|
|
+ ", unequal to both non-packed variant " + nonPackedTag
|
|
+ " and packed variant " + packedTag);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected final int computeSingularSerializedSize(Object value) {
|
|
int fieldNumber = WireFormatNano.getTagFieldNumber(tag);
|
|
switch (type) {
|
|
case TYPE_DOUBLE:
|
|
Double doubleValue = (Double) value;
|
|
return CodedOutputByteBufferNano.computeDoubleSize(fieldNumber, doubleValue);
|
|
case TYPE_FLOAT:
|
|
Float floatValue = (Float) value;
|
|
return CodedOutputByteBufferNano.computeFloatSize(fieldNumber, floatValue);
|
|
case TYPE_INT64:
|
|
Long int64Value = (Long) value;
|
|
return CodedOutputByteBufferNano.computeInt64Size(fieldNumber, int64Value);
|
|
case TYPE_UINT64:
|
|
Long uint64Value = (Long) value;
|
|
return CodedOutputByteBufferNano.computeUInt64Size(fieldNumber, uint64Value);
|
|
case TYPE_INT32:
|
|
Integer int32Value = (Integer) value;
|
|
return CodedOutputByteBufferNano.computeInt32Size(fieldNumber, int32Value);
|
|
case TYPE_FIXED64:
|
|
Long fixed64Value = (Long) value;
|
|
return CodedOutputByteBufferNano.computeFixed64Size(fieldNumber, fixed64Value);
|
|
case TYPE_FIXED32:
|
|
Integer fixed32Value = (Integer) value;
|
|
return CodedOutputByteBufferNano.computeFixed32Size(fieldNumber, fixed32Value);
|
|
case TYPE_BOOL:
|
|
Boolean boolValue = (Boolean) value;
|
|
return CodedOutputByteBufferNano.computeBoolSize(fieldNumber, boolValue);
|
|
case TYPE_STRING:
|
|
String stringValue = (String) value;
|
|
return CodedOutputByteBufferNano.computeStringSize(fieldNumber, stringValue);
|
|
case TYPE_BYTES:
|
|
byte[] bytesValue = (byte[]) value;
|
|
return CodedOutputByteBufferNano.computeBytesSize(fieldNumber, bytesValue);
|
|
case TYPE_UINT32:
|
|
Integer uint32Value = (Integer) value;
|
|
return CodedOutputByteBufferNano.computeUInt32Size(fieldNumber, uint32Value);
|
|
case TYPE_ENUM:
|
|
Integer enumValue = (Integer) value;
|
|
return CodedOutputByteBufferNano.computeEnumSize(fieldNumber, enumValue);
|
|
case TYPE_SFIXED32:
|
|
Integer sfixed32Value = (Integer) value;
|
|
return CodedOutputByteBufferNano.computeSFixed32Size(fieldNumber,
|
|
sfixed32Value);
|
|
case TYPE_SFIXED64:
|
|
Long sfixed64Value = (Long) value;
|
|
return CodedOutputByteBufferNano.computeSFixed64Size(fieldNumber,
|
|
sfixed64Value);
|
|
case TYPE_SINT32:
|
|
Integer sint32Value = (Integer) value;
|
|
return CodedOutputByteBufferNano.computeSInt32Size(fieldNumber, sint32Value);
|
|
case TYPE_SINT64:
|
|
Long sint64Value = (Long) value;
|
|
return CodedOutputByteBufferNano.computeSInt64Size(fieldNumber, sint64Value);
|
|
default:
|
|
throw new IllegalArgumentException("Unknown type " + type);
|
|
}
|
|
}
|
|
}
|
|
}
|