// 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.
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.
/**
* 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.
TODO(xiaofeng): This could be merged into CodedInputStream or CodedInputStreamReader which
is already being passed through all the parsing rountines.
/**
* 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.
/**
* 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. /** 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.
/**
* 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. /** 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. /** 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. /** 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. /** 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. /** Decodes and returns a float value. */
static float decodeFloat(byte[] data, int position) {
return Float.intBitsToFloat(decodeFixed32(data, position));
}
Decodes a string value. /** 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. /** 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. /** 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. /** 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. /** 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. /** 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. /** 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. /** 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. /** 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. /** 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. /** 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. /** 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. /** 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. /** 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. /** 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. /** 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. /** 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. /** 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. /** 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. /** 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. /** 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. /** 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. /** 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. /** 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.
/**
* 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. /** 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
Returns: The position of after read all messages
/**
* 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
Returns: The position of after read all groups
/**
* 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. /** 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. /** 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();
}
}
}