// 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.util.List;
import java.util.Map;

An adapter between the Reader interface and CodedInputStream.
/** An adapter between the {@link Reader} interface and {@link CodedInputStream}. */
@ExperimentalApi final class CodedInputStreamReader implements Reader { private static final int FIXED32_MULTIPLE_MASK = FIXED32_SIZE - 1; private static final int FIXED64_MULTIPLE_MASK = FIXED64_SIZE - 1; private static final int NEXT_TAG_UNSET = 0; private final CodedInputStream input; private int tag; private int endGroupTag; private int nextTag = NEXT_TAG_UNSET; public static CodedInputStreamReader forCodedInput(CodedInputStream input) { if (input.wrapper != null) { return input.wrapper; } return new CodedInputStreamReader(input); } private CodedInputStreamReader(CodedInputStream input) { this.input = Internal.checkNotNull(input, "input"); this.input.wrapper = this; } @Override public boolean shouldDiscardUnknownFields() { return input.shouldDiscardUnknownFields(); } @Override public int getFieldNumber() throws IOException { if (nextTag != NEXT_TAG_UNSET) { tag = nextTag; nextTag = NEXT_TAG_UNSET; } else { tag = input.readTag(); } if (tag == 0 || tag == endGroupTag) { return Reader.READ_DONE; } return WireFormat.getTagFieldNumber(tag); } @Override public int getTag() { return tag; } @Override public boolean skipField() throws IOException { if (input.isAtEnd() || tag == endGroupTag) { return false; } return input.skipField(tag); } private void requireWireType(int requiredWireType) throws IOException { if (WireFormat.getTagWireType(tag) != requiredWireType) { throw InvalidProtocolBufferException.invalidWireType(); } } @Override public double readDouble() throws IOException { requireWireType(WIRETYPE_FIXED64); return input.readDouble(); } @Override public float readFloat() throws IOException { requireWireType(WIRETYPE_FIXED32); return input.readFloat(); } @Override public long readUInt64() throws IOException { requireWireType(WIRETYPE_VARINT); return input.readUInt64(); } @Override public long readInt64() throws IOException { requireWireType(WIRETYPE_VARINT); return input.readInt64(); } @Override public int readInt32() throws IOException { requireWireType(WIRETYPE_VARINT); return input.readInt32(); } @Override public long readFixed64() throws IOException { requireWireType(WIRETYPE_FIXED64); return input.readFixed64(); } @Override public int readFixed32() throws IOException { requireWireType(WIRETYPE_FIXED32); return input.readFixed32(); } @Override public boolean readBool() throws IOException { requireWireType(WIRETYPE_VARINT); return input.readBool(); } @Override public String readString() throws IOException { requireWireType(WIRETYPE_LENGTH_DELIMITED); return input.readString(); } @Override public String readStringRequireUtf8() throws IOException { requireWireType(WIRETYPE_LENGTH_DELIMITED); return input.readStringRequireUtf8(); } @SuppressWarnings("unchecked") @Override public <T> T readMessage(Class<T> clazz, ExtensionRegistryLite extensionRegistry) throws IOException { requireWireType(WIRETYPE_LENGTH_DELIMITED); return readMessage(Protobuf.getInstance().schemaFor(clazz), extensionRegistry); } @SuppressWarnings("unchecked") @Override public <T> T readMessageBySchemaWithCheck( Schema<T> schema, ExtensionRegistryLite extensionRegistry) throws IOException { requireWireType(WIRETYPE_LENGTH_DELIMITED); return readMessage(schema, extensionRegistry); } @SuppressWarnings("unchecked") @Override public <T> T readGroup(Class<T> clazz, ExtensionRegistryLite extensionRegistry) throws IOException { requireWireType(WIRETYPE_START_GROUP); return readGroup(Protobuf.getInstance().schemaFor(clazz), extensionRegistry); } @SuppressWarnings("unchecked") @Override public <T> T readGroupBySchemaWithCheck(Schema<T> schema, ExtensionRegistryLite extensionRegistry) throws IOException { requireWireType(WIRETYPE_START_GROUP); return readGroup(schema, extensionRegistry); } // Should have the same semantics of CodedInputStream#readMessage() private <T> T readMessage(Schema<T> schema, ExtensionRegistryLite extensionRegistry) throws IOException { int size = input.readUInt32(); if (input.recursionDepth >= input.recursionLimit) { throw InvalidProtocolBufferException.recursionLimitExceeded(); } // Push the new limit. final int prevLimit = input.pushLimit(size); // Allocate and read the message. T message = schema.newInstance(); ++input.recursionDepth; schema.mergeFrom(message, this, extensionRegistry); schema.makeImmutable(message); input.checkLastTagWas(0); --input.recursionDepth; // Restore the previous limit. input.popLimit(prevLimit); return message; } 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); return input.readBytes(); } @Override public int readUInt32() throws IOException { requireWireType(WIRETYPE_VARINT); return input.readUInt32(); } @Override public int readEnum() throws IOException { requireWireType(WIRETYPE_VARINT); return input.readEnum(); } @Override public int readSFixed32() throws IOException { requireWireType(WIRETYPE_FIXED32); return input.readSFixed32(); } @Override public long readSFixed64() throws IOException { requireWireType(WIRETYPE_FIXED64); return input.readSFixed64(); } @Override public int readSInt32() throws IOException { requireWireType(WIRETYPE_VARINT); return input.readSInt32(); } @Override public long readSInt64() throws IOException { requireWireType(WIRETYPE_VARINT); return input.readSInt64(); } @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 = input.readUInt32(); verifyPackedFixed64Length(bytes); int endPos = input.getTotalBytesRead() + bytes; do { plist.addDouble(input.readDouble()); } while (input.getTotalBytesRead() < endPos); break; case WIRETYPE_FIXED64: while (true) { plist.addDouble(input.readDouble()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; return; } } default: throw InvalidProtocolBufferException.invalidWireType(); } } else { switch (WireFormat.getTagWireType(tag)) { case WIRETYPE_LENGTH_DELIMITED: final int bytes = input.readUInt32(); verifyPackedFixed64Length(bytes); int endPos = input.getTotalBytesRead() + bytes; do { target.add(input.readDouble()); } while (input.getTotalBytesRead() < endPos); break; case WIRETYPE_FIXED64: while (true) { target.add(input.readDouble()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; 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 = input.readUInt32(); verifyPackedFixed32Length(bytes); int endPos = input.getTotalBytesRead() + bytes; do { plist.addFloat(input.readFloat()); } while (input.getTotalBytesRead() < endPos); break; case WIRETYPE_FIXED32: while (true) { plist.addFloat(input.readFloat()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; return; } } default: throw InvalidProtocolBufferException.invalidWireType(); } } else { switch (WireFormat.getTagWireType(tag)) { case WIRETYPE_LENGTH_DELIMITED: final int bytes = input.readUInt32(); verifyPackedFixed32Length(bytes); int endPos = input.getTotalBytesRead() + bytes; do { target.add(input.readFloat()); } while (input.getTotalBytesRead() < endPos); break; case WIRETYPE_FIXED32: while (true) { target.add(input.readFloat()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; 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 = input.readUInt32(); int endPos = input.getTotalBytesRead() + bytes; do { plist.addLong(input.readUInt64()); } while (input.getTotalBytesRead() < endPos); requirePosition(endPos); break; case WIRETYPE_VARINT: while (true) { plist.addLong(input.readUInt64()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; return; } } default: throw InvalidProtocolBufferException.invalidWireType(); } } else { switch (WireFormat.getTagWireType(tag)) { case WIRETYPE_LENGTH_DELIMITED: final int bytes = input.readUInt32(); int endPos = input.getTotalBytesRead() + bytes; do { target.add(input.readUInt64()); } while (input.getTotalBytesRead() < endPos); requirePosition(endPos); break; case WIRETYPE_VARINT: while (true) { target.add(input.readUInt64()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; 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 = input.readUInt32(); int endPos = input.getTotalBytesRead() + bytes; do { plist.addLong(input.readInt64()); } while (input.getTotalBytesRead() < endPos); requirePosition(endPos); break; case WIRETYPE_VARINT: while (true) { plist.addLong(input.readInt64()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; return; } } default: throw InvalidProtocolBufferException.invalidWireType(); } } else { switch (WireFormat.getTagWireType(tag)) { case WIRETYPE_LENGTH_DELIMITED: final int bytes = input.readUInt32(); int endPos = input.getTotalBytesRead() + bytes; do { target.add(input.readInt64()); } while (input.getTotalBytesRead() < endPos); requirePosition(endPos); break; case WIRETYPE_VARINT: while (true) { target.add(input.readInt64()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; 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 = input.readUInt32(); int endPos = input.getTotalBytesRead() + bytes; do { plist.addInt(input.readInt32()); } while (input.getTotalBytesRead() < endPos); requirePosition(endPos); break; case WIRETYPE_VARINT: while (true) { plist.addInt(input.readInt32()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; return; } } default: throw InvalidProtocolBufferException.invalidWireType(); } } else { switch (WireFormat.getTagWireType(tag)) { case WIRETYPE_LENGTH_DELIMITED: final int bytes = input.readUInt32(); int endPos = input.getTotalBytesRead() + bytes; do { target.add(input.readInt32()); } while (input.getTotalBytesRead() < endPos); requirePosition(endPos); break; case WIRETYPE_VARINT: while (true) { target.add(input.readInt32()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; 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 = input.readUInt32(); verifyPackedFixed64Length(bytes); int endPos = input.getTotalBytesRead() + bytes; do { plist.addLong(input.readFixed64()); } while (input.getTotalBytesRead() < endPos); break; case WIRETYPE_FIXED64: while (true) { plist.addLong(input.readFixed64()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; return; } } default: throw InvalidProtocolBufferException.invalidWireType(); } } else { switch (WireFormat.getTagWireType(tag)) { case WIRETYPE_LENGTH_DELIMITED: final int bytes = input.readUInt32(); verifyPackedFixed64Length(bytes); int endPos = input.getTotalBytesRead() + bytes; do { target.add(input.readFixed64()); } while (input.getTotalBytesRead() < endPos); break; case WIRETYPE_FIXED64: while (true) { target.add(input.readFixed64()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; 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 = input.readUInt32(); verifyPackedFixed32Length(bytes); int endPos = input.getTotalBytesRead() + bytes; do { plist.addInt(input.readFixed32()); } while (input.getTotalBytesRead() < endPos); break; case WIRETYPE_FIXED32: while (true) { plist.addInt(input.readFixed32()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; return; } } default: throw InvalidProtocolBufferException.invalidWireType(); } } else { switch (WireFormat.getTagWireType(tag)) { case WIRETYPE_LENGTH_DELIMITED: final int bytes = input.readUInt32(); verifyPackedFixed32Length(bytes); int endPos = input.getTotalBytesRead() + bytes; do { target.add(input.readFixed32()); } while (input.getTotalBytesRead() < endPos); break; case WIRETYPE_FIXED32: while (true) { target.add(input.readFixed32()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; 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 = input.readUInt32(); int endPos = input.getTotalBytesRead() + bytes; do { plist.addBoolean(input.readBool()); } while (input.getTotalBytesRead() < endPos); requirePosition(endPos); break; case WIRETYPE_VARINT: while (true) { plist.addBoolean(input.readBool()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; return; } } default: throw InvalidProtocolBufferException.invalidWireType(); } } else { switch (WireFormat.getTagWireType(tag)) { case WIRETYPE_LENGTH_DELIMITED: final int bytes = input.readUInt32(); int endPos = input.getTotalBytesRead() + bytes; do { target.add(input.readBool()); } while (input.getTotalBytesRead() < endPos); requirePosition(endPos); break; case WIRETYPE_VARINT: while (true) { target.add(input.readBool()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; 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 (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; return; } } } else { while (true) { target.add(requireUtf8 ? readStringRequireUtf8() : readString()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; 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 (input.isAtEnd() || nextTag != NEXT_TAG_UNSET) { return; } int nextTag = input.readTag(); if (nextTag != listTag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; 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 (input.isAtEnd() || nextTag != NEXT_TAG_UNSET) { return; } int nextTag = input.readTag(); if (nextTag != listTag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; 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 (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; 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 = input.readUInt32(); int endPos = input.getTotalBytesRead() + bytes; do { plist.addInt(input.readUInt32()); } while (input.getTotalBytesRead() < endPos); requirePosition(endPos); break; case WIRETYPE_VARINT: while (true) { plist.addInt(input.readUInt32()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; return; } } default: throw InvalidProtocolBufferException.invalidWireType(); } } else { switch (WireFormat.getTagWireType(tag)) { case WIRETYPE_LENGTH_DELIMITED: final int bytes = input.readUInt32(); int endPos = input.getTotalBytesRead() + bytes; do { target.add(input.readUInt32()); } while (input.getTotalBytesRead() < endPos); requirePosition(endPos); break; case WIRETYPE_VARINT: while (true) { target.add(input.readUInt32()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; 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 = input.readUInt32(); int endPos = input.getTotalBytesRead() + bytes; do { plist.addInt(input.readEnum()); } while (input.getTotalBytesRead() < endPos); requirePosition(endPos); break; case WIRETYPE_VARINT: while (true) { plist.addInt(input.readEnum()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; return; } } default: throw InvalidProtocolBufferException.invalidWireType(); } } else { switch (WireFormat.getTagWireType(tag)) { case WIRETYPE_LENGTH_DELIMITED: final int bytes = input.readUInt32(); int endPos = input.getTotalBytesRead() + bytes; do { target.add(input.readEnum()); } while (input.getTotalBytesRead() < endPos); requirePosition(endPos); break; case WIRETYPE_VARINT: while (true) { target.add(input.readEnum()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; 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 = input.readUInt32(); verifyPackedFixed32Length(bytes); int endPos = input.getTotalBytesRead() + bytes; do { plist.addInt(input.readSFixed32()); } while (input.getTotalBytesRead() < endPos); break; case WIRETYPE_FIXED32: while (true) { plist.addInt(input.readSFixed32()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; return; } } default: throw InvalidProtocolBufferException.invalidWireType(); } } else { switch (WireFormat.getTagWireType(tag)) { case WIRETYPE_LENGTH_DELIMITED: final int bytes = input.readUInt32(); verifyPackedFixed32Length(bytes); int endPos = input.getTotalBytesRead() + bytes; do { target.add(input.readSFixed32()); } while (input.getTotalBytesRead() < endPos); break; case WIRETYPE_FIXED32: while (true) { target.add(input.readSFixed32()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; 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 = input.readUInt32(); verifyPackedFixed64Length(bytes); int endPos = input.getTotalBytesRead() + bytes; do { plist.addLong(input.readSFixed64()); } while (input.getTotalBytesRead() < endPos); break; case WIRETYPE_FIXED64: while (true) { plist.addLong(input.readSFixed64()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; return; } } default: throw InvalidProtocolBufferException.invalidWireType(); } } else { switch (WireFormat.getTagWireType(tag)) { case WIRETYPE_LENGTH_DELIMITED: final int bytes = input.readUInt32(); verifyPackedFixed64Length(bytes); int endPos = input.getTotalBytesRead() + bytes; do { target.add(input.readSFixed64()); } while (input.getTotalBytesRead() < endPos); break; case WIRETYPE_FIXED64: while (true) { target.add(input.readSFixed64()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; 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 = input.readUInt32(); int endPos = input.getTotalBytesRead() + bytes; do { plist.addInt(input.readSInt32()); } while (input.getTotalBytesRead() < endPos); requirePosition(endPos); break; case WIRETYPE_VARINT: while (true) { plist.addInt(input.readSInt32()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; return; } } default: throw InvalidProtocolBufferException.invalidWireType(); } } else { switch (WireFormat.getTagWireType(tag)) { case WIRETYPE_LENGTH_DELIMITED: final int bytes = input.readUInt32(); int endPos = input.getTotalBytesRead() + bytes; do { target.add(input.readSInt32()); } while (input.getTotalBytesRead() < endPos); requirePosition(endPos); break; case WIRETYPE_VARINT: while (true) { target.add(input.readSInt32()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; 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 = input.readUInt32(); int endPos = input.getTotalBytesRead() + bytes; do { plist.addLong(input.readSInt64()); } while (input.getTotalBytesRead() < endPos); requirePosition(endPos); break; case WIRETYPE_VARINT: while (true) { plist.addLong(input.readSInt64()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; return; } } default: throw InvalidProtocolBufferException.invalidWireType(); } } else { switch (WireFormat.getTagWireType(tag)) { case WIRETYPE_LENGTH_DELIMITED: final int bytes = input.readUInt32(); int endPos = input.getTotalBytesRead() + bytes; do { target.add(input.readSInt64()); } while (input.getTotalBytesRead() < endPos); requirePosition(endPos); break; case WIRETYPE_VARINT: while (true) { target.add(input.readSInt64()); if (input.isAtEnd()) { return; } int nextTag = input.readTag(); if (nextTag != tag) { // We've reached the end of the repeated field. Save the next tag value. this.nextTag = nextTag; return; } } default: throw InvalidProtocolBufferException.invalidWireType(); } } } private void verifyPackedFixed64Length(int bytes) throws IOException { if ((bytes & FIXED64_MULTIPLE_MASK) != 0) { // Require that the number of bytes be a multiple of 8. throw InvalidProtocolBufferException.parseFailure(); } } @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 = input.readUInt32(); final int prevLimit = input.pushLimit(size); K key = metadata.defaultKey; V value = metadata.defaultValue; try { while (true) { int number = getFieldNumber(); if (number == READ_DONE || input.isAtEnd()) { 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 previous limit. input.popLimit(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."); } } private void verifyPackedFixed32Length(int bytes) throws IOException { 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 (input.getTotalBytesRead() != expectedPosition) { throw InvalidProtocolBufferException.truncatedMessage(); } } }