com.google.protobuf.ArrayDecoders Maven / Gradle / Ivy
// 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.GeneratedMessageLite.ExtensionDescriptor;
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 routines utilizing these functions must catch
* IndexOutOfBoundsException and convert it to protobuf's InvalidProtocolBufferException when
* crossing protobuf public API boundaries.
*/
@CheckReturnValue
final class ArrayDecoders {
private 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.
*/
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 {
Object msg = schema.newInstance();
int offset = mergeMessageField(msg, schema, data, position, limit, registers);
schema.makeImmutable(msg);
registers.object1 = msg;
return offset;
}
/** Decodes a group value. */
@SuppressWarnings({"unchecked", "rawtypes"})
static int decodeGroupField(
Schema schema, byte[] data, int position, int limit, int endGroup, Registers registers)
throws IOException {
Object msg = schema.newInstance();
int offset = mergeGroupField(msg, schema, data, position, limit, endGroup, registers);
schema.makeImmutable(msg);
registers.object1 = msg;
return offset;
}
@SuppressWarnings({"unchecked", "rawtypes"})
static int mergeMessageField(
Object msg, 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();
}
schema.mergeFrom(msg, data, position, position + length, registers);
registers.object1 = msg;
return position + length;
}
@SuppressWarnings({"unchecked", "rawtypes"})
static int mergeGroupField(
Object msg,
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;
// It's OK to directly use parseProto2Message since proto3 doesn't have group.
final int endPosition =
messageSchema.parseProto2Message(msg, data, position, limit, endGroup, registers);
registers.object1 = msg;
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. */
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 output = (ProtobufList) 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 output = (ProtobufList) 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 output = (ProtobufList) 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