blob: d64574c2a581e3019ccdab2ba8e661d6443aa3b5 [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.WireFormat.FIXED32_SIZE;
import static com.google.protobuf.WireFormat.FIXED64_SIZE;
import static com.google.protobuf.WireFormat.WIRETYPE_END_GROUP;
import static com.google.protobuf.WireFormat.WIRETYPE_FIXED32;
import static com.google.protobuf.WireFormat.WIRETYPE_FIXED64;
import static com.google.protobuf.WireFormat.WIRETYPE_LENGTH_DELIMITED;
import static com.google.protobuf.WireFormat.WIRETYPE_START_GROUP;
import static com.google.protobuf.WireFormat.WIRETYPE_VARINT;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Map;
/**
* A {@link Reader} that reads from a buffer containing a message serialized with the binary
* protocol.
*/
@ExperimentalApi
abstract class BinaryReader implements Reader {
private static final int FIXED32_MULTIPLE_MASK = FIXED32_SIZE - 1;
private static final int FIXED64_MULTIPLE_MASK = FIXED64_SIZE - 1;
/**
* Creates a new reader using the given {@code buffer} as input.
*
* @param buffer the input buffer. The buffer (including position, limit, etc.) will not be
* modified. To increment the buffer position after the read completes, use the value returned
* by {@link #getTotalBytesRead()}.
* @param bufferIsImmutable if {@code true} the reader assumes that the content of {@code buffer}
* will never change and any allocated {@link ByteString} instances will by directly wrap
* slices of {@code buffer}.
* @return the reader
*/
public static BinaryReader newInstance(ByteBuffer buffer, boolean bufferIsImmutable) {
if (buffer.hasArray()) {
// TODO(nathanmittler): Add support for unsafe operations.
return new SafeHeapReader(buffer, bufferIsImmutable);
}
// TODO(nathanmittler): Add support for direct buffers
throw new IllegalArgumentException("Direct buffers not yet supported");
}
/** Only allow subclassing for inner classes. */
private BinaryReader() {}
/** Returns the total number of bytes read so far from the input buffer. */
public abstract int getTotalBytesRead();
@Override
public boolean shouldDiscardUnknownFields() {
return false;
}
/**
* A {@link BinaryReader} implementation that operates on a heap {@link ByteBuffer}. Uses only
* safe operations on the underlying array.
*/
private static final class SafeHeapReader extends BinaryReader {
private final boolean bufferIsImmutable;
private final byte[] buffer;
private int pos;
private final int initialPos;
private int limit;
private int tag;
private int endGroupTag;
public SafeHeapReader(ByteBuffer bytebuf, boolean bufferIsImmutable) {
this.bufferIsImmutable = bufferIsImmutable;
buffer = bytebuf.array();
initialPos = pos = bytebuf.arrayOffset() + bytebuf.position();
limit = bytebuf.arrayOffset() + bytebuf.limit();
}
private boolean isAtEnd() {
return pos == limit;
}
@Override
public int getTotalBytesRead() {
return pos - initialPos;
}
@Override
public int getFieldNumber() throws IOException {
if (isAtEnd()) {
return Reader.READ_DONE;
}
tag = readVarint32();
if (tag == endGroupTag) {
return Reader.READ_DONE;
}
return WireFormat.getTagFieldNumber(tag);
}
@Override
public int getTag() {
return tag;
}
@Override
public boolean skipField() throws IOException {
if (isAtEnd() || tag == endGroupTag) {
return false;
}
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_VARINT:
skipVarint();
return true;
case WIRETYPE_FIXED64:
skipBytes(FIXED64_SIZE);
return true;
case WIRETYPE_LENGTH_DELIMITED:
skipBytes(readVarint32());
return true;
case WIRETYPE_FIXED32:
skipBytes(FIXED32_SIZE);
return true;
case WIRETYPE_START_GROUP:
skipGroup();
return true;
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
@Override
public double readDouble() throws IOException {
requireWireType(WIRETYPE_FIXED64);
return Double.longBitsToDouble(readLittleEndian64());
}
@Override
public float readFloat() throws IOException {
requireWireType(WIRETYPE_FIXED32);
return Float.intBitsToFloat(readLittleEndian32());
}
@Override
public long readUInt64() throws IOException {
requireWireType(WIRETYPE_VARINT);
return readVarint64();
}
@Override
public long readInt64() throws IOException {
requireWireType(WIRETYPE_VARINT);
return readVarint64();
}
@Override
public int readInt32() throws IOException {
requireWireType(WIRETYPE_VARINT);
return readVarint32();
}
@Override
public long readFixed64() throws IOException {
requireWireType(WIRETYPE_FIXED64);
return readLittleEndian64();
}
@Override
public int readFixed32() throws IOException {
requireWireType(WIRETYPE_FIXED32);
return readLittleEndian32();
}
@Override
public boolean readBool() throws IOException {
requireWireType(WIRETYPE_VARINT);
return readVarint32() != 0;
}
@Override
public String readString() throws IOException {
return readStringInternal(false);
}
@Override
public String readStringRequireUtf8() throws IOException {
return readStringInternal(true);
}
public String readStringInternal(boolean requireUtf8) throws IOException {
requireWireType(WIRETYPE_LENGTH_DELIMITED);
final int size = readVarint32();
if (size == 0) {
return "";
}
requireBytes(size);
if (requireUtf8 && !Utf8.isValidUtf8(buffer, pos, pos + size)) {
throw InvalidProtocolBufferException.invalidUtf8();
}
String result = new String(buffer, pos, size, Internal.UTF_8);
pos += size;
return result;
}
@Override
public <T> T readMessage(Class<T> clazz, ExtensionRegistryLite extensionRegistry)
throws IOException {
requireWireType(WIRETYPE_LENGTH_DELIMITED);
return readMessage(Protobuf.getInstance().schemaFor(clazz), extensionRegistry);
}
@Override
public <T> T readMessageBySchemaWithCheck(
Schema<T> schema, ExtensionRegistryLite extensionRegistry) throws IOException {
requireWireType(WIRETYPE_LENGTH_DELIMITED);
return readMessage(schema, extensionRegistry);
}
private <T> T readMessage(Schema<T> schema, ExtensionRegistryLite extensionRegistry)
throws IOException {
int size = readVarint32();
requireBytes(size);
// Update the limit.
int prevLimit = limit;
int newLimit = pos + size;
limit = newLimit;
try {
// Allocate and read the message.
T message = schema.newInstance();
schema.mergeFrom(message, this, extensionRegistry);
schema.makeImmutable(message);
if (pos != newLimit) {
throw InvalidProtocolBufferException.parseFailure();
}
return message;
} finally {
// Restore the limit.
limit = prevLimit;
}
}
@Override
public <T> T readGroup(Class<T> clazz, ExtensionRegistryLite extensionRegistry)
throws IOException {
requireWireType(WIRETYPE_START_GROUP);
return readGroup(Protobuf.getInstance().schemaFor(clazz), extensionRegistry);
}
@Override
public <T> T readGroupBySchemaWithCheck(
Schema<T> schema, ExtensionRegistryLite extensionRegistry) throws IOException {
requireWireType(WIRETYPE_START_GROUP);
return readGroup(schema, extensionRegistry);
}
private <T> T readGroup(Schema<T> schema, ExtensionRegistryLite extensionRegistry)
throws IOException {
int prevEndGroupTag = endGroupTag;
endGroupTag = WireFormat.makeTag(WireFormat.getTagFieldNumber(tag), WIRETYPE_END_GROUP);
try {
// Allocate and read the message.
T message = schema.newInstance();
schema.mergeFrom(message, this, extensionRegistry);
schema.makeImmutable(message);
if (tag != endGroupTag) {
throw InvalidProtocolBufferException.parseFailure();
}
return message;
} finally {
// Restore the old end group tag.
endGroupTag = prevEndGroupTag;
}
}
@Override
public ByteString readBytes() throws IOException {
requireWireType(WIRETYPE_LENGTH_DELIMITED);
int size = readVarint32();
if (size == 0) {
return ByteString.EMPTY;
}
requireBytes(size);
ByteString bytes =
bufferIsImmutable
? ByteString.wrap(buffer, pos, size)
: ByteString.copyFrom(buffer, pos, size);
pos += size;
return bytes;
}
@Override
public int readUInt32() throws IOException {
requireWireType(WIRETYPE_VARINT);
return readVarint32();
}
@Override
public int readEnum() throws IOException {
requireWireType(WIRETYPE_VARINT);
return readVarint32();
}
@Override
public int readSFixed32() throws IOException {
requireWireType(WIRETYPE_FIXED32);
return readLittleEndian32();
}
@Override
public long readSFixed64() throws IOException {
requireWireType(WIRETYPE_FIXED64);
return readLittleEndian64();
}
@Override
public int readSInt32() throws IOException {
requireWireType(WIRETYPE_VARINT);
return CodedInputStream.decodeZigZag32(readVarint32());
}
@Override
public long readSInt64() throws IOException {
requireWireType(WIRETYPE_VARINT);
return CodedInputStream.decodeZigZag64(readVarint64());
}
@Override
public void readDoubleList(List<Double> target) throws IOException {
if (target instanceof DoubleArrayList) {
DoubleArrayList plist = (DoubleArrayList) target;
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
verifyPackedFixed64Length(bytes);
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
plist.addDouble(Double.longBitsToDouble(readLittleEndian64_NoCheck()));
}
break;
case WIRETYPE_FIXED64:
while (true) {
plist.addDouble(readDouble());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
} else {
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
verifyPackedFixed64Length(bytes);
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
target.add(Double.longBitsToDouble(readLittleEndian64_NoCheck()));
}
break;
case WIRETYPE_FIXED64:
while (true) {
target.add(readDouble());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
}
@Override
public void readFloatList(List<Float> target) throws IOException {
if (target instanceof FloatArrayList) {
FloatArrayList plist = (FloatArrayList) target;
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
verifyPackedFixed32Length(bytes);
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
plist.addFloat(Float.intBitsToFloat(readLittleEndian32_NoCheck()));
}
break;
case WIRETYPE_FIXED32:
while (true) {
plist.addFloat(readFloat());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
} else {
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
verifyPackedFixed32Length(bytes);
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
target.add(Float.intBitsToFloat(readLittleEndian32_NoCheck()));
}
break;
case WIRETYPE_FIXED32:
while (true) {
target.add(readFloat());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
}
@Override
public void readUInt64List(List<Long> target) throws IOException {
if (target instanceof LongArrayList) {
LongArrayList plist = (LongArrayList) target;
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
plist.addLong(readVarint64());
}
requirePosition(fieldEndPos);
break;
case WIRETYPE_VARINT:
while (true) {
plist.addLong(readUInt64());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
} else {
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
target.add(readVarint64());
}
requirePosition(fieldEndPos);
break;
case WIRETYPE_VARINT:
while (true) {
target.add(readUInt64());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
}
@Override
public void readInt64List(List<Long> target) throws IOException {
if (target instanceof LongArrayList) {
LongArrayList plist = (LongArrayList) target;
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
plist.addLong(readVarint64());
}
requirePosition(fieldEndPos);
break;
case WIRETYPE_VARINT:
while (true) {
plist.addLong(readInt64());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
} else {
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
target.add(readVarint64());
}
requirePosition(fieldEndPos);
break;
case WIRETYPE_VARINT:
while (true) {
target.add(readInt64());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
}
@Override
public void readInt32List(List<Integer> target) throws IOException {
if (target instanceof IntArrayList) {
IntArrayList plist = (IntArrayList) target;
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
plist.addInt(readVarint32());
}
requirePosition(fieldEndPos);
break;
case WIRETYPE_VARINT:
while (true) {
plist.addInt(readInt32());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
} else {
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
target.add(readVarint32());
}
requirePosition(fieldEndPos);
break;
case WIRETYPE_VARINT:
while (true) {
target.add(readInt32());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
}
@Override
public void readFixed64List(List<Long> target) throws IOException {
if (target instanceof LongArrayList) {
LongArrayList plist = (LongArrayList) target;
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
verifyPackedFixed64Length(bytes);
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
plist.addLong(readLittleEndian64_NoCheck());
}
break;
case WIRETYPE_FIXED64:
while (true) {
plist.addLong(readFixed64());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
} else {
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
verifyPackedFixed64Length(bytes);
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
target.add(readLittleEndian64_NoCheck());
}
break;
case WIRETYPE_FIXED64:
while (true) {
target.add(readFixed64());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
}
@Override
public void readFixed32List(List<Integer> target) throws IOException {
if (target instanceof IntArrayList) {
IntArrayList plist = (IntArrayList) target;
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
verifyPackedFixed32Length(bytes);
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
plist.addInt(readLittleEndian32_NoCheck());
}
break;
case WIRETYPE_FIXED32:
while (true) {
plist.addInt(readFixed32());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
} else {
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
verifyPackedFixed32Length(bytes);
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
target.add(readLittleEndian32_NoCheck());
}
break;
case WIRETYPE_FIXED32:
while (true) {
target.add(readFixed32());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
}
@Override
public void readBoolList(List<Boolean> target) throws IOException {
if (target instanceof BooleanArrayList) {
BooleanArrayList plist = (BooleanArrayList) target;
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
plist.addBoolean(readVarint32() != 0);
}
requirePosition(fieldEndPos);
break;
case WIRETYPE_VARINT:
while (true) {
plist.addBoolean(readBool());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
} else {
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
target.add(readVarint32() != 0);
}
requirePosition(fieldEndPos);
break;
case WIRETYPE_VARINT:
while (true) {
target.add(readBool());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
}
@Override
public void readStringList(List<String> target) throws IOException {
readStringListInternal(target, false);
}
@Override
public void readStringListRequireUtf8(List<String> target) throws IOException {
readStringListInternal(target, true);
}
public void readStringListInternal(List<String> target, boolean requireUtf8)
throws IOException {
if (WireFormat.getTagWireType(tag) != WIRETYPE_LENGTH_DELIMITED) {
throw InvalidProtocolBufferException.invalidWireType();
}
if (target instanceof LazyStringList && !requireUtf8) {
LazyStringList lazyList = (LazyStringList) target;
while (true) {
lazyList.add(readBytes());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
} else {
while (true) {
target.add(readStringInternal(requireUtf8));
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
}
}
@Override
public <T> void readMessageList(
List<T> target, Class<T> targetType, ExtensionRegistryLite extensionRegistry)
throws IOException {
final Schema<T> schema = Protobuf.getInstance().schemaFor(targetType);
readMessageList(target, schema, extensionRegistry);
}
@Override
public <T> void readMessageList(
List<T> target, Schema<T> schema, ExtensionRegistryLite extensionRegistry)
throws IOException {
if (WireFormat.getTagWireType(tag) != WIRETYPE_LENGTH_DELIMITED) {
throw InvalidProtocolBufferException.invalidWireType();
}
final int listTag = tag;
while (true) {
target.add(readMessage(schema, extensionRegistry));
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != listTag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
}
@Override
public <T> void readGroupList(
List<T> target, Class<T> targetType, ExtensionRegistryLite extensionRegistry)
throws IOException {
final Schema<T> schema = Protobuf.getInstance().schemaFor(targetType);
readGroupList(target, schema, extensionRegistry);
}
@Override
public <T> void readGroupList(
List<T> target, Schema<T> schema, ExtensionRegistryLite extensionRegistry)
throws IOException {
if (WireFormat.getTagWireType(tag) != WIRETYPE_START_GROUP) {
throw InvalidProtocolBufferException.invalidWireType();
}
final int listTag = tag;
while (true) {
target.add(readGroup(schema, extensionRegistry));
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != listTag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
}
@Override
public void readBytesList(List<ByteString> target) throws IOException {
if (WireFormat.getTagWireType(tag) != WIRETYPE_LENGTH_DELIMITED) {
throw InvalidProtocolBufferException.invalidWireType();
}
while (true) {
target.add(readBytes());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
}
@Override
public void readUInt32List(List<Integer> target) throws IOException {
if (target instanceof IntArrayList) {
IntArrayList plist = (IntArrayList) target;
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
plist.addInt(readVarint32());
}
break;
case WIRETYPE_VARINT:
while (true) {
plist.addInt(readUInt32());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
} else {
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
target.add(readVarint32());
}
break;
case WIRETYPE_VARINT:
while (true) {
target.add(readUInt32());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
}
@Override
public void readEnumList(List<Integer> target) throws IOException {
if (target instanceof IntArrayList) {
IntArrayList plist = (IntArrayList) target;
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
plist.addInt(readVarint32());
}
break;
case WIRETYPE_VARINT:
while (true) {
plist.addInt(readEnum());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
} else {
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
target.add(readVarint32());
}
break;
case WIRETYPE_VARINT:
while (true) {
target.add(readEnum());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
}
@Override
public void readSFixed32List(List<Integer> target) throws IOException {
if (target instanceof IntArrayList) {
IntArrayList plist = (IntArrayList) target;
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
verifyPackedFixed32Length(bytes);
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
plist.addInt(readLittleEndian32_NoCheck());
}
break;
case WIRETYPE_FIXED32:
while (true) {
plist.addInt(readSFixed32());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
} else {
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
verifyPackedFixed32Length(bytes);
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
target.add(readLittleEndian32_NoCheck());
}
break;
case WIRETYPE_FIXED32:
while (true) {
target.add(readSFixed32());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
}
@Override
public void readSFixed64List(List<Long> target) throws IOException {
if (target instanceof LongArrayList) {
LongArrayList plist = (LongArrayList) target;
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
verifyPackedFixed64Length(bytes);
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
plist.addLong(readLittleEndian64_NoCheck());
}
break;
case WIRETYPE_FIXED64:
while (true) {
plist.addLong(readSFixed64());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
} else {
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
verifyPackedFixed64Length(bytes);
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
target.add(readLittleEndian64_NoCheck());
}
break;
case WIRETYPE_FIXED64:
while (true) {
target.add(readSFixed64());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
}
@Override
public void readSInt32List(List<Integer> target) throws IOException {
if (target instanceof IntArrayList) {
IntArrayList plist = (IntArrayList) target;
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
plist.addInt(CodedInputStream.decodeZigZag32(readVarint32()));
}
break;
case WIRETYPE_VARINT:
while (true) {
plist.addInt(readSInt32());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
} else {
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
target.add(CodedInputStream.decodeZigZag32(readVarint32()));
}
break;
case WIRETYPE_VARINT:
while (true) {
target.add(readSInt32());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
}
@Override
public void readSInt64List(List<Long> target) throws IOException {
if (target instanceof LongArrayList) {
LongArrayList plist = (LongArrayList) target;
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
plist.addLong(CodedInputStream.decodeZigZag64(readVarint64()));
}
break;
case WIRETYPE_VARINT:
while (true) {
plist.addLong(readSInt64());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
} else {
switch (WireFormat.getTagWireType(tag)) {
case WIRETYPE_LENGTH_DELIMITED:
final int bytes = readVarint32();
final int fieldEndPos = pos + bytes;
while (pos < fieldEndPos) {
target.add(CodedInputStream.decodeZigZag64(readVarint64()));
}
break;
case WIRETYPE_VARINT:
while (true) {
target.add(readSInt64());
if (isAtEnd()) {
return;
}
int prevPos = pos;
int nextTag = readVarint32();
if (nextTag != tag) {
// We've reached the end of the repeated field. Rewind the buffer position to before
// the new tag.
pos = prevPos;
return;
}
}
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
}
@SuppressWarnings("unchecked")
@Override
public <K, V> void readMap(
Map<K, V> target,
MapEntryLite.Metadata<K, V> metadata,
ExtensionRegistryLite extensionRegistry)
throws IOException {
requireWireType(WIRETYPE_LENGTH_DELIMITED);
int size = readVarint32();
requireBytes(size);
// Update the limit.
int prevLimit = limit;
int newLimit = pos + size;
limit = newLimit;
try {
K key = metadata.defaultKey;
V value = metadata.defaultValue;
while (true) {
int number = getFieldNumber();
if (number == READ_DONE) {
break;
}
try {
switch (number) {
case 1:
key = (K) readField(metadata.keyType, null, null);
break;
case 2:
value =
(V)
readField(
metadata.valueType,
metadata.defaultValue.getClass(),
extensionRegistry);
break;
default:
if (!skipField()) {
throw new InvalidProtocolBufferException("Unable to parse map entry.");
}
break;
}
} catch (InvalidProtocolBufferException.InvalidWireTypeException ignore) {
// the type doesn't match, skip the field.
if (!skipField()) {
throw new InvalidProtocolBufferException("Unable to parse map entry.");
}
}
}
target.put(key, value);
} finally {
// Restore the limit.
limit = prevLimit;
}
}
private Object readField(
WireFormat.FieldType fieldType,
Class<?> messageType,
ExtensionRegistryLite extensionRegistry)
throws IOException {
switch (fieldType) {
case BOOL:
return readBool();
case BYTES:
return readBytes();
case DOUBLE:
return readDouble();
case ENUM:
return readEnum();
case FIXED32:
return readFixed32();
case FIXED64:
return readFixed64();
case FLOAT:
return readFloat();
case INT32:
return readInt32();
case INT64:
return readInt64();
case MESSAGE:
return readMessage(messageType, extensionRegistry);
case SFIXED32:
return readSFixed32();
case SFIXED64:
return readSFixed64();
case SINT32:
return readSInt32();
case SINT64:
return readSInt64();
case STRING:
return readStringRequireUtf8();
case UINT32:
return readUInt32();
case UINT64:
return readUInt64();
default:
throw new RuntimeException("unsupported field type.");
}
}
/** Read a raw Varint from the stream. If larger than 32 bits, discard the upper bits. */
private int readVarint32() throws IOException {
// See implementation notes for readRawVarint64
int i = pos;
if (limit == pos) {
throw InvalidProtocolBufferException.truncatedMessage();
}
int x;
if ((x = buffer[i++]) >= 0) {
pos = i;
return x;
} else if (limit - i < 9) {
return (int) readVarint64SlowPath();
} else if ((x ^= (buffer[i++] << 7)) < 0) {
x ^= (~0 << 7);
} else if ((x ^= (buffer[i++] << 14)) >= 0) {
x ^= (~0 << 7) ^ (~0 << 14);
} else if ((x ^= (buffer[i++] << 21)) < 0) {
x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21);
} else {
int y = buffer[i++];
x ^= y << 28;
x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21) ^ (~0 << 28);
if (y < 0
&& buffer[i++] < 0
&& buffer[i++] < 0
&& buffer[i++] < 0
&& buffer[i++] < 0
&& buffer[i++] < 0) {
throw InvalidProtocolBufferException.malformedVarint();
}
}
pos = i;
return x;
}
public long readVarint64() throws IOException {
// Implementation notes:
//
// Optimized for one-byte values, expected to be common.
// The particular code below was selected from various candidates
// empirically, by winning VarintBenchmark.
//
// Sign extension of (signed) Java bytes is usually a nuisance, but
// we exploit it here to more easily obtain the sign of bytes read.
// Instead of cleaning up the sign extension bits by masking eagerly,
// we delay until we find the final (positive) byte, when we clear all
// accumulated bits with one xor. We depend on javac to constant fold.
int i = pos;
if (limit == i) {
throw InvalidProtocolBufferException.truncatedMessage();
}
final byte[] buffer = this.buffer;
long x;
int y;
if ((y = buffer[i++]) >= 0) {
pos = i;
return y;
} else if (limit - i < 9) {
return readVarint64SlowPath();
} else if ((y ^= (buffer[i++] << 7)) < 0) {
x = y ^ (~0 << 7);
} else if ((y ^= (buffer[i++] << 14)) >= 0) {
x = y ^ ((~0 << 7) ^ (~0 << 14));
} else if ((y ^= (buffer[i++] << 21)) < 0) {
x = y ^ ((~0 << 7) ^ (~0 << 14) ^ (~0 << 21));
} else if ((x = y ^ ((long) buffer[i++] << 28)) >= 0L) {
x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28);
} else if ((x ^= ((long) buffer[i++] << 35)) < 0L) {
x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35);
} else if ((x ^= ((long) buffer[i++] << 42)) >= 0L) {
x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42);
} else if ((x ^= ((long) buffer[i++] << 49)) < 0L) {
x ^=
(~0L << 7)
^ (~0L << 14)
^ (~0L << 21)
^ (~0L << 28)
^ (~0L << 35)
^ (~0L << 42)
^ (~0L << 49);
} else {
x ^= ((long) buffer[i++] << 56);
x ^=
(~0L << 7)
^ (~0L << 14)
^ (~0L << 21)
^ (~0L << 28)
^ (~0L << 35)
^ (~0L << 42)
^ (~0L << 49)
^ (~0L << 56);
if (x < 0L) {
if (buffer[i++] < 0L) {
throw InvalidProtocolBufferException.malformedVarint();
}
}
}
pos = i;
return x;
}
private long readVarint64SlowPath() throws IOException {
long result = 0;
for (int shift = 0; shift < 64; shift += 7) {
final byte b = readByte();
result |= (long) (b & 0x7F) << shift;
if ((b & 0x80) == 0) {
return result;
}
}
throw InvalidProtocolBufferException.malformedVarint();
}
private byte readByte() throws IOException {
if (pos == limit) {
throw InvalidProtocolBufferException.truncatedMessage();
}
return buffer[pos++];
}
private int readLittleEndian32() throws IOException {
requireBytes(FIXED32_SIZE);
return readLittleEndian32_NoCheck();
}
private long readLittleEndian64() throws IOException {
requireBytes(FIXED64_SIZE);
return readLittleEndian64_NoCheck();
}
private int readLittleEndian32_NoCheck() {
int p = pos;
final byte[] buffer = this.buffer;
pos = p + FIXED32_SIZE;
return (((buffer[p] & 0xff))
| ((buffer[p + 1] & 0xff) << 8)
| ((buffer[p + 2] & 0xff) << 16)
| ((buffer[p + 3] & 0xff) << 24));
}
private long readLittleEndian64_NoCheck() {
int p = pos;
final byte[] buffer = this.buffer;
pos = p + FIXED64_SIZE;
return (((buffer[p] & 0xffL))
| ((buffer[p + 1] & 0xffL) << 8)
| ((buffer[p + 2] & 0xffL) << 16)
| ((buffer[p + 3] & 0xffL) << 24)
| ((buffer[p + 4] & 0xffL) << 32)
| ((buffer[p + 5] & 0xffL) << 40)
| ((buffer[p + 6] & 0xffL) << 48)
| ((buffer[p + 7] & 0xffL) << 56));
}
private void skipVarint() throws IOException {
if (limit - pos >= 10) {
final byte[] buffer = this.buffer;
int p = pos;
for (int i = 0; i < 10; i++) {
if (buffer[p++] >= 0) {
pos = p;
return;
}
}
}
skipVarintSlowPath();
}
private void skipVarintSlowPath() throws IOException {
for (int i = 0; i < 10; i++) {
if (readByte() >= 0) {
return;
}
}
throw InvalidProtocolBufferException.malformedVarint();
}
private void skipBytes(final int size) throws IOException {
requireBytes(size);
pos += size;
}
private void skipGroup() throws IOException {
int prevEndGroupTag = endGroupTag;
endGroupTag = WireFormat.makeTag(WireFormat.getTagFieldNumber(tag), WIRETYPE_END_GROUP);
while (true) {
if (getFieldNumber() == READ_DONE || !skipField()) {
break;
}
}
if (tag != endGroupTag) {
throw InvalidProtocolBufferException.parseFailure();
}
endGroupTag = prevEndGroupTag;
}
private void requireBytes(int size) throws IOException {
if (size < 0 || size > (limit - pos)) {
throw InvalidProtocolBufferException.truncatedMessage();
}
}
private void requireWireType(int requiredWireType) throws IOException {
if (WireFormat.getTagWireType(tag) != requiredWireType) {
throw InvalidProtocolBufferException.invalidWireType();
}
}
private void verifyPackedFixed64Length(int bytes) throws IOException {
requireBytes(bytes);
if ((bytes & FIXED64_MULTIPLE_MASK) != 0) {
// Require that the number of bytes be a multiple of 8.
throw InvalidProtocolBufferException.parseFailure();
}
}
private void verifyPackedFixed32Length(int bytes) throws IOException {
requireBytes(bytes);
if ((bytes & FIXED32_MULTIPLE_MASK) != 0) {
// Require that the number of bytes be a multiple of 4.
throw InvalidProtocolBufferException.parseFailure();
}
}
private void requirePosition(int expectedPosition) throws IOException {
if (pos != expectedPosition) {
throw InvalidProtocolBufferException.truncatedMessage();
}
}
}
}