blob: a969c04c5f0b5707edc394c15c5a146b7d60879b [file] [log] [blame]
// Protocol Buffers - Google's data interchange format
// Copyright 2008 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;
import static com.google.protobuf.MessageSchema.getMutableUnknownFields;
import com.google.protobuf.Internal.ProtobufList;
import java.io.IOException;
/**
* Helper functions to decode protobuf wire format from a byte array.
*
* <p>Note that these functions don't do boundary check on the byte array but instead rely on Java
* VM to check it. That means parsing rountines utilizing these functions must catch
* IndexOutOfBoundsException and convert it to protobuf's InvalidProtocolBufferException when
* crossing protobuf public API boundaries.
*/
final class ArrayDecoders {
/**
* A helper used to return multiple values in a Java function. Java doesn't natively support
* returning multiple values in a function. Creating a new Object to hold the return values will
* be too expensive. Instead, we pass a Registers instance to functions that want to return
* multiple values and let the function set the return value in this Registers instance instead.
*
* <p>TODO(xiaofeng): This could be merged into CodedInputStream or CodedInputStreamReader which
* is already being passed through all the parsing rountines.
*/
static final class Registers {
public int int1;
public long long1;
public Object object1;
public final ExtensionRegistryLite extensionRegistry;
Registers() {
this.extensionRegistry = ExtensionRegistryLite.getEmptyRegistry();
}
Registers(ExtensionRegistryLite extensionRegistry) {
if (extensionRegistry == null) {
throw new NullPointerException();
}
this.extensionRegistry = extensionRegistry;
}
}
/**
* Decodes a varint. Returns the position after the varint. The decoded varint is stored in
* registers.int1.
*/
static int decodeVarint32(byte[] data, int position, Registers registers) {
int value = data[position++];
if (value >= 0) {
registers.int1 = value;
return position;
}
return decodeVarint32(value, data, position, registers);
}
/** Like decodeVarint32 except that the first byte is already read. */
static int decodeVarint32(int firstByte, byte[] data, int position, Registers registers) {
int value = firstByte & 0x7F;
final byte b2 = data[position++];
if (b2 >= 0) {
registers.int1 = value | ((int) b2 << 7);
return position;
}
value |= (b2 & 0x7F) << 7;
final byte b3 = data[position++];
if (b3 >= 0) {
registers.int1 = value | ((int) b3 << 14);
return position;
}
value |= (b3 & 0x7F) << 14;
final byte b4 = data[position++];
if (b4 >= 0) {
registers.int1 = value | ((int) b4 << 21);
return position;
}
value |= (b4 & 0x7F) << 21;
final byte b5 = data[position++];
if (b5 >= 0) {
registers.int1 = value | ((int) b5 << 28);
return position;
}
value |= (b5 & 0x7F) << 28;
while (data[position++] < 0) {}
registers.int1 = value;
return position;
}
/**
* Decodes a varint. Returns the position after the varint. The decoded varint is stored in
* registers.long1.
*/
static int decodeVarint64(byte[] data, int position, Registers registers) {
long value = data[position++];
if (value >= 0) {
registers.long1 = value;
return position;
} else {
return decodeVarint64(value, data, position, registers);
}
}
/** Like decodeVarint64 except that the first byte is already read. */
static int decodeVarint64(long firstByte, byte[] data, int position, Registers registers) {
long value = firstByte & 0x7F;
byte next = data[position++];
int shift = 7;
value |= (long) (next & 0x7F) << 7;
while (next < 0) {
next = data[position++];
shift += 7;
value |= (long) (next & 0x7F) << shift;
}
registers.long1 = value;
return position;
}
/** Decodes and returns a fixed32 value. */
static int decodeFixed32(byte[] data, int position) {
return (data[position] & 0xff)
| ((data[position + 1] & 0xff) << 8)
| ((data[position + 2] & 0xff) << 16)
| ((data[position + 3] & 0xff) << 24);
}
/** Decodes and returns a fixed64 value. */
static long decodeFixed64(byte[] data, int position) {
return (data[position] & 0xffL)
| ((data[position + 1] & 0xffL) << 8)
| ((data[position + 2] & 0xffL) << 16)
| ((data[position + 3] & 0xffL) << 24)
| ((data[position + 4] & 0xffL) << 32)
| ((data[position + 5] & 0xffL) << 40)
| ((data[position + 6] & 0xffL) << 48)
| ((data[position + 7] & 0xffL) << 56);
}
/** Decodes and returns a double value. */
static double decodeDouble(byte[] data, int position) {
return Double.longBitsToDouble(decodeFixed64(data, position));
}
/** Decodes and returns a float value. */
static float decodeFloat(byte[] data, int position) {
return Float.intBitsToFloat(decodeFixed32(data, position));
}
/** Decodes a string value. */
static int decodeString(byte[] data, int position, Registers registers)
throws InvalidProtocolBufferException {
position = decodeVarint32(data, position, registers);
final int length = registers.int1;
if (length < 0) {
throw InvalidProtocolBufferException.negativeSize();
} else if (length == 0) {
registers.object1 = "";
return position;
} else {
registers.object1 = new String(data, position, length, Internal.UTF_8);
return position + length;
}
}
/** Decodes a string value with utf8 check. */
static int decodeStringRequireUtf8(byte[] data, int position, Registers registers)
throws InvalidProtocolBufferException {
position = decodeVarint32(data, position, registers);
final int length = registers.int1;
if (length < 0) {
throw InvalidProtocolBufferException.negativeSize();
} else if (length == 0) {
registers.object1 = "";
return position;
} else {
registers.object1 = Utf8.decodeUtf8(data, position, length);
return position + length;
}
}
/** Decodes a bytes value. */
static int decodeBytes(byte[] data, int position, Registers registers)
throws InvalidProtocolBufferException {
position = decodeVarint32(data, position, registers);
final int length = registers.int1;
if (length < 0) {
throw InvalidProtocolBufferException.negativeSize();
} else if (length > data.length - position) {
throw InvalidProtocolBufferException.truncatedMessage();
} else if (length == 0) {
registers.object1 = ByteString.EMPTY;
return position;
} else {
registers.object1 = ByteString.copyFrom(data, position, length);
return position + length;
}
}
/** Decodes a message value. */
@SuppressWarnings({"unchecked", "rawtypes"})
static int decodeMessageField(
Schema schema, byte[] data, int position, int limit, Registers registers) throws IOException {
int length = data[position++];
if (length < 0) {
position = decodeVarint32(length, data, position, registers);
length = registers.int1;
}
if (length < 0 || length > limit - position) {
throw InvalidProtocolBufferException.truncatedMessage();
}
Object result = schema.newInstance();
schema.mergeFrom(result, data, position, position + length, registers);
schema.makeImmutable(result);
registers.object1 = result;
return position + length;
}
/** Decodes a group value. */
@SuppressWarnings({"unchecked", "rawtypes"})
static int decodeGroupField(
Schema schema, byte[] data, int position, int limit, int endGroup, Registers registers)
throws IOException {
// A group field must has a MessageSchema (the only other subclass of Schema is MessageSetSchema
// and it can't be used in group fields).
final MessageSchema messageSchema = (MessageSchema) schema;
Object result = messageSchema.newInstance();
// It's OK to directly use parseProto2Message since proto3 doesn't have group.
final int endPosition =
messageSchema.parseProto2Message(result, data, position, limit, endGroup, registers);
messageSchema.makeImmutable(result);
registers.object1 = result;
return endPosition;
}
/** Decodes a repeated 32-bit varint field. Returns the position after all read values. */
static int decodeVarint32List(
int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) {
final IntArrayList output = (IntArrayList) list;
position = decodeVarint32(data, position, registers);
output.addInt(registers.int1);
while (position < limit) {
int nextPosition = decodeVarint32(data, position, registers);
if (tag != registers.int1) {
break;
}
position = decodeVarint32(data, nextPosition, registers);
output.addInt(registers.int1);
}
return position;
}
/** Decodes a repeated 64-bit varint field. Returns the position after all read values. */
static int decodeVarint64List(
int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) {
final LongArrayList output = (LongArrayList) list;
position = decodeVarint64(data, position, registers);
output.addLong(registers.long1);
while (position < limit) {
int nextPosition = decodeVarint32(data, position, registers);
if (tag != registers.int1) {
break;
}
position = decodeVarint64(data, nextPosition, registers);
output.addLong(registers.long1);
}
return position;
}
/** Decodes a repeated fixed32 field. Returns the position after all read values. */
static int decodeFixed32List(
int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) {
final IntArrayList output = (IntArrayList) list;
output.addInt(decodeFixed32(data, position));
position += 4;
while (position < limit) {
int nextPosition = decodeVarint32(data, position, registers);
if (tag != registers.int1) {
break;
}
output.addInt(decodeFixed32(data, nextPosition));
position = nextPosition + 4;
}
return position;
}
/** Decodes a repeated fixed64 field. Returns the position after all read values. */
static int decodeFixed64List(
int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) {
final LongArrayList output = (LongArrayList) list;
output.addLong(decodeFixed64(data, position));
position += 8;
while (position < limit) {
int nextPosition = decodeVarint32(data, position, registers);
if (tag != registers.int1) {
break;
}
output.addLong(decodeFixed64(data, nextPosition));
position = nextPosition + 8;
}
return position;
}
/** Decodes a repeated float field. Returns the position after all read values. */
static int decodeFloatList(
int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) {
final FloatArrayList output = (FloatArrayList) list;
output.addFloat(decodeFloat(data, position));
position += 4;
while (position < limit) {
int nextPosition = decodeVarint32(data, position, registers);
if (tag != registers.int1) {
break;
}
output.addFloat(decodeFloat(data, nextPosition));
position = nextPosition + 4;
}
return position;
}
/** Decodes a repeated double field. Returns the position after all read values. */
static int decodeDoubleList(
int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) {
final DoubleArrayList output = (DoubleArrayList) list;
output.addDouble(decodeDouble(data, position));
position += 8;
while (position < limit) {
int nextPosition = decodeVarint32(data, position, registers);
if (tag != registers.int1) {
break;
}
output.addDouble(decodeDouble(data, nextPosition));
position = nextPosition + 8;
}
return position;
}
/** Decodes a repeated boolean field. Returns the position after all read values. */
static int decodeBoolList(
int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) {
final BooleanArrayList output = (BooleanArrayList) list;
position = decodeVarint64(data, position, registers);
output.addBoolean(registers.long1 != 0);
while (position < limit) {
int nextPosition = decodeVarint32(data, position, registers);
if (tag != registers.int1) {
break;
}
position = decodeVarint64(data, nextPosition, registers);
output.addBoolean(registers.long1 != 0);
}
return position;
}
/** Decodes a repeated sint32 field. Returns the position after all read values. */
static int decodeSInt32List(
int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) {
final IntArrayList output = (IntArrayList) list;
position = decodeVarint32(data, position, registers);
output.addInt(CodedInputStream.decodeZigZag32(registers.int1));
while (position < limit) {
int nextPosition = decodeVarint32(data, position, registers);
if (tag != registers.int1) {
break;
}
position = decodeVarint32(data, nextPosition, registers);
output.addInt(CodedInputStream.decodeZigZag32(registers.int1));
}
return position;
}
/** Decodes a repeated sint64 field. Returns the position after all read values. */
static int decodeSInt64List(
int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) {
final LongArrayList output = (LongArrayList) list;
position = decodeVarint64(data, position, registers);
output.addLong(CodedInputStream.decodeZigZag64(registers.long1));
while (position < limit) {
int nextPosition = decodeVarint32(data, position, registers);
if (tag != registers.int1) {
break;
}
position = decodeVarint64(data, nextPosition, registers);
output.addLong(CodedInputStream.decodeZigZag64(registers.long1));
}
return position;
}
/** Decodes a packed 32-bit varint field. Returns the position after all read values. */
static int decodePackedVarint32List(
byte[] data, int position, ProtobufList<?> list, Registers registers) throws IOException {
final IntArrayList output = (IntArrayList) list;
position = decodeVarint32(data, position, registers);
final int fieldLimit = position + registers.int1;
while (position < fieldLimit) {
position = decodeVarint32(data, position, registers);
output.addInt(registers.int1);
}
if (position != fieldLimit) {
throw InvalidProtocolBufferException.truncatedMessage();
}
return position;
}
/** Decodes a packed 64-bit varint field. Returns the position after all read values. */
static int decodePackedVarint64List(
byte[] data, int position, ProtobufList<?> list, Registers registers) throws IOException {
final LongArrayList output = (LongArrayList) list;
position = decodeVarint32(data, position, registers);
final int fieldLimit = position + registers.int1;
while (position < fieldLimit) {
position = decodeVarint64(data, position, registers);
output.addLong(registers.long1);
}
if (position != fieldLimit) {
throw InvalidProtocolBufferException.truncatedMessage();
}
return position;
}
/** Decodes a packed fixed32 field. Returns the position after all read values. */
static int decodePackedFixed32List(
byte[] data, int position, ProtobufList<?> list, Registers registers) throws IOException {
final IntArrayList output = (IntArrayList) list;
position = decodeVarint32(data, position, registers);
final int fieldLimit = position + registers.int1;
while (position < fieldLimit) {
output.addInt(decodeFixed32(data, position));
position += 4;
}
if (position != fieldLimit) {
throw InvalidProtocolBufferException.truncatedMessage();
}
return position;
}
/** Decodes a packed fixed64 field. Returns the position after all read values. */
static int decodePackedFixed64List(
byte[] data, int position, ProtobufList<?> list, Registers registers) throws IOException {
final LongArrayList output = (LongArrayList) list;
position = decodeVarint32(data, position, registers);
final int fieldLimit = position + registers.int1;
while (position < fieldLimit) {
output.addLong(decodeFixed64(data, position));
position += 8;
}
if (position != fieldLimit) {
throw InvalidProtocolBufferException.truncatedMessage();
}
return position;
}
/** Decodes a packed float field. Returns the position after all read values. */
static int decodePackedFloatList(
byte[] data, int position, ProtobufList<?> list, Registers registers) throws IOException {
final FloatArrayList output = (FloatArrayList) list;
position = decodeVarint32(data, position, registers);
final int fieldLimit = position + registers.int1;
while (position < fieldLimit) {
output.addFloat(decodeFloat(data, position));
position += 4;
}
if (position != fieldLimit) {
throw InvalidProtocolBufferException.truncatedMessage();
}
return position;
}
/** Decodes a packed double field. Returns the position after all read values. */
static int decodePackedDoubleList(
byte[] data, int position, ProtobufList<?> list, Registers registers) throws IOException {
final DoubleArrayList output = (DoubleArrayList) list;
position = decodeVarint32(data, position, registers);
final int fieldLimit = position + registers.int1;
while (position < fieldLimit) {
output.addDouble(decodeDouble(data, position));
position += 8;
}
if (position != fieldLimit) {
throw InvalidProtocolBufferException.truncatedMessage();
}
return position;
}
/** Decodes a packed boolean field. Returns the position after all read values. */
static int decodePackedBoolList(
byte[] data, int position, ProtobufList<?> list, Registers registers) throws IOException {
final BooleanArrayList output = (BooleanArrayList) list;
position = decodeVarint32(data, position, registers);
final int fieldLimit = position + registers.int1;
while (position < fieldLimit) {
position = decodeVarint64(data, position, registers);
output.addBoolean(registers.long1 != 0);
}
if (position != fieldLimit) {
throw InvalidProtocolBufferException.truncatedMessage();
}
return position;
}
/** Decodes a packed sint32 field. Returns the position after all read values. */
static int decodePackedSInt32List(
byte[] data, int position, ProtobufList<?> list, Registers registers) throws IOException {
final IntArrayList output = (IntArrayList) list;
position = decodeVarint32(data, position, registers);
final int fieldLimit = position + registers.int1;
while (position < fieldLimit) {
position = decodeVarint32(data, position, registers);
output.addInt(CodedInputStream.decodeZigZag32(registers.int1));
}
if (position != fieldLimit) {
throw InvalidProtocolBufferException.truncatedMessage();
}
return position;
}
/** Decodes a packed sint64 field. Returns the position after all read values. */
@SuppressWarnings("unchecked")
static int decodePackedSInt64List(
byte[] data, int position, ProtobufList<?> list, Registers registers) throws IOException {
final LongArrayList output = (LongArrayList) list;
position = decodeVarint32(data, position, registers);
final int fieldLimit = position + registers.int1;
while (position < fieldLimit) {
position = decodeVarint64(data, position, registers);
output.addLong(CodedInputStream.decodeZigZag64(registers.long1));
}
if (position != fieldLimit) {
throw InvalidProtocolBufferException.truncatedMessage();
}
return position;
}
/** Decodes a repeated string field. Returns the position after all read values. */
@SuppressWarnings("unchecked")
static int decodeStringList(
int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers)
throws InvalidProtocolBufferException {
final ProtobufList<String> output = (ProtobufList<String>) list;
position = decodeVarint32(data, position, registers);
final int length = registers.int1;
if (length < 0) {
throw InvalidProtocolBufferException.negativeSize();
} else if (length == 0) {
output.add("");
} else {
String value = new String(data, position, length, Internal.UTF_8);
output.add(value);
position += length;
}
while (position < limit) {
int nextPosition = decodeVarint32(data, position, registers);
if (tag != registers.int1) {
break;
}
position = decodeVarint32(data, nextPosition, registers);
final int nextLength = registers.int1;
if (nextLength < 0) {
throw InvalidProtocolBufferException.negativeSize();
} else if (nextLength == 0) {
output.add("");
} else {
String value = new String(data, position, nextLength, Internal.UTF_8);
output.add(value);
position += nextLength;
}
}
return position;
}
/**
* Decodes a repeated string field with utf8 check. Returns the position after all read values.
*/
@SuppressWarnings("unchecked")
static int decodeStringListRequireUtf8(
int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers)
throws InvalidProtocolBufferException {
final ProtobufList<String> output = (ProtobufList<String>) list;
position = decodeVarint32(data, position, registers);
final int length = registers.int1;
if (length < 0) {
throw InvalidProtocolBufferException.negativeSize();
} else if (length == 0) {
output.add("");
} else {
if (!Utf8.isValidUtf8(data, position, position + length)) {
throw InvalidProtocolBufferException.invalidUtf8();
}
String value = new String(data, position, length, Internal.UTF_8);
output.add(value);
position += length;
}
while (position < limit) {
int nextPosition = decodeVarint32(data, position, registers);
if (tag != registers.int1) {
break;
}
position = decodeVarint32(data, nextPosition, registers);
final int nextLength = registers.int1;
if (nextLength < 0) {
throw InvalidProtocolBufferException.negativeSize();
} else if (nextLength == 0) {
output.add("");
} else {
if (!Utf8.isValidUtf8(data, position, position + nextLength)) {
throw InvalidProtocolBufferException.invalidUtf8();
}
String value = new String(data, position, nextLength, Internal.UTF_8);
output.add(value);
position += nextLength;
}
}
return position;
}
/** Decodes a repeated bytes field. Returns the position after all read values. */
@SuppressWarnings("unchecked")
static int decodeBytesList(
int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers)
throws InvalidProtocolBufferException {
final ProtobufList<ByteString> output = (ProtobufList<ByteString>) list;
position = decodeVarint32(data, position, registers);
final int length = registers.int1;
if (length < 0) {
throw InvalidProtocolBufferException.negativeSize();
} else if (length > data.length - position) {
throw InvalidProtocolBufferException.truncatedMessage();
} else if (length == 0) {
output.add(ByteString.EMPTY);
} else {
output.add(ByteString.copyFrom(data, position, length));
position += length;
}
while (position < limit) {
int nextPosition = decodeVarint32(data, position, registers);
if (tag != registers.int1) {
break;
}
position = decodeVarint32(data, nextPosition, registers);
final int nextLength = registers.int1;
if (nextLength < 0) {
throw InvalidProtocolBufferException.negativeSize();
} else if (nextLength > data.length - position) {
throw InvalidProtocolBufferException.truncatedMessage();
} else if (nextLength == 0) {
output.add(ByteString.EMPTY);
} else {
output.add(ByteString.copyFrom(data, position, nextLength));
position += nextLength;
}
}
return position;
}
/**
* Decodes a repeated message field
*
* @return The position of after read all messages
*/
@SuppressWarnings({"unchecked"})
static int decodeMessageList(
Schema<?> schema,
int tag,
byte[] data,
int position,
int limit,
ProtobufList<?> list,
Registers registers)
throws IOException {
final ProtobufList<Object> output = (ProtobufList<Object>) list;
position = decodeMessageField(schema, data, position, limit, registers);
output.add(registers.object1);
while (position < limit) {
int nextPosition = decodeVarint32(data, position, registers);
if (tag != registers.int1) {
break;
}
position = decodeMessageField(schema, data, nextPosition, limit, registers);
output.add(registers.object1);
}
return position;
}
/**
* Decodes a repeated group field
*
* @return The position of after read all groups
*/
@SuppressWarnings({"unchecked", "rawtypes"})
static int decodeGroupList(
Schema schema,
int tag,
byte[] data,
int position,
int limit,
ProtobufList<?> list,
Registers registers)
throws IOException {
final ProtobufList<Object> output = (ProtobufList<Object>) list;
final int endgroup = (tag & ~0x7) | WireFormat.WIRETYPE_END_GROUP;
position = decodeGroupField(schema, data, position, limit, endgroup, registers);
output.add(registers.object1);
while (position < limit) {
int nextPosition = decodeVarint32(data, position, registers);
if (tag != registers.int1) {
break;
}
position = decodeGroupField(schema, data, nextPosition, limit, endgroup, registers);
output.add(registers.object1);
}
return position;
}
static int decodeExtensionOrUnknownField(
int tag, byte[] data, int position, int limit,
Object message,
MessageLite defaultInstance,
UnknownFieldSchema<UnknownFieldSetLite, UnknownFieldSetLite> unknownFieldSchema,
Registers registers)
throws IOException {
final int number = tag >>> 3;
GeneratedMessageLite.GeneratedExtension extension =
registers.extensionRegistry.findLiteExtensionByNumber(defaultInstance, number);
if (extension == null) {
return decodeUnknownField(
tag, data, position, limit, getMutableUnknownFields(message), registers);
} else {
((GeneratedMessageLite.ExtendableMessage<?, ?>) message).ensureExtensionsAreMutable();
return decodeExtension(
tag, data, position, limit, (GeneratedMessageLite.ExtendableMessage) message,
extension, unknownFieldSchema, registers);
}
}
static int decodeExtension(
int tag,
byte[] data,
int position,
int limit,
GeneratedMessageLite.ExtendableMessage<?, ?> message,
GeneratedMessageLite.GeneratedExtension<?, ?> extension,
UnknownFieldSchema<UnknownFieldSetLite, UnknownFieldSetLite> unknownFieldSchema,
Registers registers)
throws IOException {
final FieldSet<GeneratedMessageLite.ExtensionDescriptor> extensions = message.extensions;
final int fieldNumber = tag >>> 3;
if (extension.descriptor.isRepeated() && extension.descriptor.isPacked()) {
switch (extension.getLiteType()) {
case DOUBLE:
{
DoubleArrayList list = new DoubleArrayList();
position = decodePackedDoubleList(data, position, list, registers);
extensions.setField(extension.descriptor, list);
break;
}
case FLOAT:
{
FloatArrayList list = new FloatArrayList();
position = decodePackedFloatList(data, position, list, registers);
extensions.setField(extension.descriptor, list);
break;
}
case INT64:
case UINT64:
{
LongArrayList list = new LongArrayList();
position = decodePackedVarint64List(data, position, list, registers);
extensions.setField(extension.descriptor, list);
break;
}
case INT32:
case UINT32:
{
IntArrayList list = new IntArrayList();
position = decodePackedVarint32List(data, position, list, registers);
extensions.setField(extension.descriptor, list);
break;
}
case FIXED64:
case SFIXED64:
{
LongArrayList list = new LongArrayList();
position = decodePackedFixed64List(data, position, list, registers);
extensions.setField(extension.descriptor, list);
break;
}
case FIXED32:
case SFIXED32:
{
IntArrayList list = new IntArrayList();
position = decodePackedFixed32List(data, position, list, registers);
extensions.setField(extension.descriptor, list);
break;
}
case BOOL:
{
BooleanArrayList list = new BooleanArrayList();
position = decodePackedBoolList(data, position, list, registers);
extensions.setField(extension.descriptor, list);
break;
}
case SINT32:
{
IntArrayList list = new IntArrayList();
position = decodePackedSInt32List(data, position, list, registers);
extensions.setField(extension.descriptor, list);
break;
}
case SINT64:
{
LongArrayList list = new LongArrayList();
position = decodePackedSInt64List(data, position, list, registers);
extensions.setField(extension.descriptor, list);
break;
}
case ENUM:
{
IntArrayList list = new IntArrayList();
position = decodePackedVarint32List(data, position, list, registers);
UnknownFieldSetLite unknownFields = message.unknownFields;
if (unknownFields == UnknownFieldSetLite.getDefaultInstance()) {
unknownFields = null;
}
unknownFields =
SchemaUtil.filterUnknownEnumList(
fieldNumber,
list,
extension.descriptor.getEnumType(),
unknownFields,
unknownFieldSchema);
if (unknownFields != null) {
message.unknownFields = unknownFields;
}
extensions.setField(extension.descriptor, list);
break;
}
default:
throw new IllegalStateException(
"Type cannot be packed: " + extension.descriptor.getLiteType());
}
} else {
Object value = null;
// Enum is a special case becasue unknown enum values will be put into UnknownFieldSetLite.
if (extension.getLiteType() == WireFormat.FieldType.ENUM) {
position = decodeVarint32(data, position, registers);
Object enumValue = extension.descriptor.getEnumType().findValueByNumber(registers.int1);
if (enumValue == null) {
UnknownFieldSetLite unknownFields = ((GeneratedMessageLite) message).unknownFields;
if (unknownFields == UnknownFieldSetLite.getDefaultInstance()) {
unknownFields = UnknownFieldSetLite.newInstance();
((GeneratedMessageLite) message).unknownFields = unknownFields;
}
SchemaUtil.storeUnknownEnum(
fieldNumber, registers.int1, unknownFields, unknownFieldSchema);
return position;
}
// Note, we store the integer value instead of the actual enum object in FieldSet.
// This is also different from full-runtime where we store EnumValueDescriptor.
value = registers.int1;
} else {
switch (extension.getLiteType()) {
case DOUBLE:
value = decodeDouble(data, position);
position += 8;
break;
case FLOAT:
value = decodeFloat(data, position);
position += 4;
break;
case INT64:
case UINT64:
position = decodeVarint64(data, position, registers);
value = registers.long1;
break;
case INT32:
case UINT32:
position = decodeVarint32(data, position, registers);
value = registers.int1;
break;
case FIXED64:
case SFIXED64:
value = decodeFixed64(data, position);
position += 8;
break;
case FIXED32:
case SFIXED32:
value = decodeFixed32(data, position);
position += 4;
break;
case BOOL:
position = decodeVarint64(data, position, registers);
value = (registers.long1 != 0);
break;
case BYTES:
position = decodeBytes(data, position, registers);
value = registers.object1;
break;
case SINT32:
position = decodeVarint32(data, position, registers);
value = CodedInputStream.decodeZigZag32(registers.int1);
break;
case SINT64:
position = decodeVarint64(data, position, registers);
value = CodedInputStream.decodeZigZag64(registers.long1);
break;
case STRING:
position = decodeString(data, position, registers);
value = registers.object1;
break;
case GROUP:
final int endTag = (fieldNumber << 3) | WireFormat.WIRETYPE_END_GROUP;
position = decodeGroupField(
Protobuf.getInstance().schemaFor(extension.getMessageDefaultInstance().getClass()),
data, position, limit, endTag, registers);
value = registers.object1;
break;
case MESSAGE:
position = decodeMessageField(
Protobuf.getInstance().schemaFor(extension.getMessageDefaultInstance().getClass()),
data, position, limit, registers);
value = registers.object1;
break;
case ENUM:
throw new IllegalStateException("Shouldn't reach here.");
}
}
if (extension.isRepeated()) {
extensions.addRepeatedField(extension.descriptor, value);
} else {
switch (extension.getLiteType()) {
case MESSAGE:
case GROUP:
Object oldValue = extensions.getField(extension.descriptor);
if (oldValue != null) {
value = Internal.mergeMessage(oldValue, value);
}
break;
default:
break;
}
extensions.setField(extension.descriptor, value);
}
}
return position;
}
/** Decodes an unknown field. */
static int decodeUnknownField(
int tag,
byte[] data,
int position,
int limit,
UnknownFieldSetLite unknownFields,
Registers registers)
throws InvalidProtocolBufferException {
if (WireFormat.getTagFieldNumber(tag) == 0) {
throw InvalidProtocolBufferException.invalidTag();
}
switch (WireFormat.getTagWireType(tag)) {
case WireFormat.WIRETYPE_VARINT:
position = decodeVarint64(data, position, registers);
unknownFields.storeField(tag, registers.long1);
return position;
case WireFormat.WIRETYPE_FIXED32:
unknownFields.storeField(tag, decodeFixed32(data, position));
return position + 4;
case WireFormat.WIRETYPE_FIXED64:
unknownFields.storeField(tag, decodeFixed64(data, position));
return position + 8;
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
position = decodeVarint32(data, position, registers);
final int length = registers.int1;
if (length < 0) {
throw InvalidProtocolBufferException.negativeSize();
} else if (length > data.length - position) {
throw InvalidProtocolBufferException.truncatedMessage();
} else if (length == 0) {
unknownFields.storeField(tag, ByteString.EMPTY);
} else {
unknownFields.storeField(tag, ByteString.copyFrom(data, position, length));
}
return position + length;
case WireFormat.WIRETYPE_START_GROUP:
final UnknownFieldSetLite child = UnknownFieldSetLite.newInstance();
final int endGroup = (tag & ~0x7) | WireFormat.WIRETYPE_END_GROUP;
int lastTag = 0;
while (position < limit) {
position = decodeVarint32(data, position, registers);
lastTag = registers.int1;
if (lastTag == endGroup) {
break;
}
position = decodeUnknownField(lastTag, data, position, limit, child, registers);
}
if (position > limit || lastTag != endGroup) {
throw InvalidProtocolBufferException.parseFailure();
}
unknownFields.storeField(tag, child);
return position;
default:
throw InvalidProtocolBufferException.invalidTag();
}
}
/** Skips an unknown field. */
static int skipField(int tag, byte[] data, int position, int limit, Registers registers)
throws InvalidProtocolBufferException {
if (WireFormat.getTagFieldNumber(tag) == 0) {
throw InvalidProtocolBufferException.invalidTag();
}
switch (WireFormat.getTagWireType(tag)) {
case WireFormat.WIRETYPE_VARINT:
position = decodeVarint64(data, position, registers);
return position;
case WireFormat.WIRETYPE_FIXED32:
return position + 4;
case WireFormat.WIRETYPE_FIXED64:
return position + 8;
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
position = decodeVarint32(data, position, registers);
return position + registers.int1;
case WireFormat.WIRETYPE_START_GROUP:
final int endGroup = (tag & ~0x7) | WireFormat.WIRETYPE_END_GROUP;
int lastTag = 0;
while (position < limit) {
position = decodeVarint32(data, position, registers);
lastTag = registers.int1;
if (lastTag == endGroup) {
break;
}
position = skipField(lastTag, data, position, limit, registers);
}
if (position > limit || lastTag != endGroup) {
throw InvalidProtocolBufferException.parseFailure();
}
return position;
default:
throw InvalidProtocolBufferException.invalidTag();
}
}
}