All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.protobuf.CodedInputStream Maven / Gradle / Ivy

Go to download

Kotlin core Protocol Buffers library. Protocol Buffers are a way of encoding structured data in an efficient yet extensible format.

There is a newer version: 4.29.0-RC2
Show newest version
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

package com.google.protobuf;

import static com.google.protobuf.Internal.EMPTY_BYTE_ARRAY;
import static com.google.protobuf.Internal.EMPTY_BYTE_BUFFER;
import static com.google.protobuf.Internal.UTF_8;
import static com.google.protobuf.Internal.checkNotNull;
import static com.google.protobuf.WireFormat.FIXED32_SIZE;
import static com.google.protobuf.WireFormat.FIXED64_SIZE;
import static com.google.protobuf.WireFormat.MAX_VARINT_SIZE;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
 * Reads and decodes protocol message fields.
 *
 * 

This class contains two kinds of methods: methods that read specific protocol message * constructs and field types (e.g. {@link #readTag()} and {@link #readInt32()}) and methods that * read low-level values (e.g. {@link #readRawVarint32()} and {@link #readRawBytes}). If you are * reading encoded protocol messages, you should use the former methods, but if you are reading some * other format of your own design, use the latter. * * @author [email protected] Kenton Varda */ public abstract class CodedInputStream { private static final int DEFAULT_BUFFER_SIZE = 4096; // Integer.MAX_VALUE == 0x7FFFFFF == INT_MAX from limits.h private static final int DEFAULT_SIZE_LIMIT = Integer.MAX_VALUE; private static volatile int defaultRecursionLimit = 100; /** Visible for subclasses. See setRecursionLimit() */ int recursionDepth; int recursionLimit = defaultRecursionLimit; /** Visible for subclasses. See setSizeLimit() */ int sizeLimit = DEFAULT_SIZE_LIMIT; /** Used to adapt to the experimental {@link Reader} interface. */ CodedInputStreamReader wrapper; /** Create a new CodedInputStream wrapping the given InputStream. */ public static CodedInputStream newInstance(final InputStream input) { return newInstance(input, DEFAULT_BUFFER_SIZE); } /** Create a new CodedInputStream wrapping the given InputStream, with a specified buffer size. */ public static CodedInputStream newInstance(final InputStream input, int bufferSize) { if (bufferSize <= 0) { throw new IllegalArgumentException("bufferSize must be > 0"); } if (input == null) { // Ideally we would throw here. This is done for backward compatibility. return newInstance(EMPTY_BYTE_ARRAY); } return new StreamDecoder(input, bufferSize); } /** Create a new CodedInputStream wrapping the given {@code Iterable }. */ public static CodedInputStream newInstance(final Iterable input) { if (!UnsafeDirectNioDecoder.isSupported()) { return newInstance(new IterableByteBufferInputStream(input)); } return newInstance(input, false); } /** Create a new CodedInputStream wrapping the given {@code Iterable }. */ static CodedInputStream newInstance( final Iterable bufs, final boolean bufferIsImmutable) { // flag is to check the type of input's ByteBuffers. // flag equals 1: all ByteBuffers have array. // flag equals 2: all ByteBuffers are direct ByteBuffers. // flag equals 3: some ByteBuffers are direct and some have array. // flag greater than 3: other cases. int flag = 0; // Total size of the input int totalSize = 0; for (ByteBuffer buf : bufs) { totalSize += buf.remaining(); if (buf.hasArray()) { flag |= 1; } else if (buf.isDirect()) { flag |= 2; } else { flag |= 4; } } if (flag == 2) { return new IterableDirectByteBufferDecoder(bufs, totalSize, bufferIsImmutable); } else { // TODO: add another decoders to deal case 1 and 3. return newInstance(new IterableByteBufferInputStream(bufs)); } } /** Create a new CodedInputStream wrapping the given byte array. */ public static CodedInputStream newInstance(final byte[] buf) { return newInstance(buf, 0, buf.length); } /** Create a new CodedInputStream wrapping the given byte array slice. */ public static CodedInputStream newInstance(final byte[] buf, final int off, final int len) { return newInstance(buf, off, len, /* bufferIsImmutable= */ false); } /** Create a new CodedInputStream wrapping the given byte array slice. */ static CodedInputStream newInstance( final byte[] buf, final int off, final int len, final boolean bufferIsImmutable) { ArrayDecoder result = new ArrayDecoder(buf, off, len, bufferIsImmutable); try { // Some uses of CodedInputStream can be more efficient if they know // exactly how many bytes are available. By pushing the end point of the // buffer as a limit, we allow them to get this information via // getBytesUntilLimit(). Pushing a limit that we know is at the end of // the stream can never hurt, since we can never past that point anyway. result.pushLimit(len); } catch (InvalidProtocolBufferException ex) { // The only reason pushLimit() might throw an exception here is if len // is negative. Normally pushLimit()'s parameter comes directly off the // wire, so it's important to catch exceptions in case of corrupt or // malicious data. However, in this case, we expect that len is not a // user-supplied value, so we can assume that it being negative indicates // a programming error. Therefore, throwing an unchecked exception is // appropriate. throw new IllegalArgumentException(ex); } return result; } /** * Create a new CodedInputStream wrapping the given ByteBuffer. The data starting from the * ByteBuffer's current position to its limit will be read. The returned CodedInputStream may or * may not share the underlying data in the ByteBuffer, therefore the ByteBuffer cannot be changed * while the CodedInputStream is in use. Note that the ByteBuffer's position won't be changed by * this function. Concurrent calls with the same ByteBuffer object are safe if no other thread is * trying to alter the ByteBuffer's status. */ public static CodedInputStream newInstance(ByteBuffer buf) { return newInstance(buf, /* bufferIsImmutable= */ false); } /** Create a new CodedInputStream wrapping the given buffer. */ static CodedInputStream newInstance(ByteBuffer buf, boolean bufferIsImmutable) { if (buf.hasArray()) { return newInstance( buf.array(), buf.arrayOffset() + buf.position(), buf.remaining(), bufferIsImmutable); } if (buf.isDirect() && UnsafeDirectNioDecoder.isSupported()) { return new UnsafeDirectNioDecoder(buf, bufferIsImmutable); } // The buffer is non-direct and does not expose the underlying array. Using the ByteBuffer API // to access individual bytes is very slow, so just copy the buffer to an array. // TODO: Re-evaluate with Java 9 byte[] buffer = new byte[buf.remaining()]; buf.duplicate().get(buffer); return newInstance(buffer, 0, buffer.length, true); } public void checkRecursionLimit() throws InvalidProtocolBufferException { if (recursionDepth >= recursionLimit) { throw InvalidProtocolBufferException.recursionLimitExceeded(); } } /** Disable construction/inheritance outside of this class. */ private CodedInputStream() {} // ----------------------------------------------------------------- /** * Attempt to read a field tag, returning zero if we have reached EOF. Protocol message parsers * use this to read tags, since a protocol message may legally end wherever a tag occurs, and zero * is not a valid tag number. */ public abstract int readTag() throws IOException; /** * Verifies that the last call to readTag() returned the given tag value. This is used to verify * that a nested group ended with the correct end tag. * * @throws InvalidProtocolBufferException {@code value} does not match the last tag. */ public abstract void checkLastTagWas(final int value) throws InvalidProtocolBufferException; public abstract int getLastTag(); /** * Reads and discards a single field, given its tag value. * * @return {@code false} if the tag is an endgroup tag, in which case nothing is skipped. * Otherwise, returns {@code true}. */ public abstract boolean skipField(final int tag) throws IOException; /** * Reads a single field and writes it to output in wire format, given its tag value. * * @return {@code false} if the tag is an endgroup tag, in which case nothing is skipped. * Otherwise, returns {@code true}. * @deprecated use {@code UnknownFieldSet} or {@code UnknownFieldSetLite} to skip to an output * stream. */ @Deprecated public abstract boolean skipField(final int tag, final CodedOutputStream output) throws IOException; /** * Reads and discards an entire message. This will read either until EOF or until an endgroup tag, * whichever comes first. */ public void skipMessage() throws IOException { while (true) { final int tag = readTag(); if (tag == 0) { return; } checkRecursionLimit(); ++recursionDepth; boolean fieldSkipped = skipField(tag); --recursionDepth; if (!fieldSkipped) { return; } } } /** * Reads an entire message and writes it to output in wire format. This will read either until EOF * or until an endgroup tag, whichever comes first. */ public void skipMessage(CodedOutputStream output) throws IOException { while (true) { final int tag = readTag(); if (tag == 0) { return; } checkRecursionLimit(); ++recursionDepth; boolean fieldSkipped = skipField(tag, output); --recursionDepth; if (!fieldSkipped) { return; } } } // ----------------------------------------------------------------- /** Read a {@code double} field value from the stream. */ public abstract double readDouble() throws IOException; /** Read a {@code float} field value from the stream. */ public abstract float readFloat() throws IOException; /** Read a {@code uint64} field value from the stream. */ public abstract long readUInt64() throws IOException; /** Read an {@code int64} field value from the stream. */ public abstract long readInt64() throws IOException; /** Read an {@code int32} field value from the stream. */ public abstract int readInt32() throws IOException; /** Read a {@code fixed64} field value from the stream. */ public abstract long readFixed64() throws IOException; /** Read a {@code fixed32} field value from the stream. */ public abstract int readFixed32() throws IOException; /** Read a {@code bool} field value from the stream. */ public abstract boolean readBool() throws IOException; /** * Read a {@code string} field value from the stream. If the stream contains malformed UTF-8, * replace the offending bytes with the standard UTF-8 replacement character. */ public abstract String readString() throws IOException; /** * Read a {@code string} field value from the stream. If the stream contains malformed UTF-8, * throw exception {@link InvalidProtocolBufferException}. */ public abstract String readStringRequireUtf8() throws IOException; /** Read a {@code group} field value from the stream. */ public abstract void readGroup( final int fieldNumber, final MessageLite.Builder builder, final ExtensionRegistryLite extensionRegistry) throws IOException; /** Read a {@code group} field value from the stream. */ public abstract T readGroup( final int fieldNumber, final Parser parser, final ExtensionRegistryLite extensionRegistry) throws IOException; /** * Reads a {@code group} field value from the stream and merges it into the given {@link * UnknownFieldSet}. * * @deprecated UnknownFieldSet.Builder now implements MessageLite.Builder, so you can just call * {@link #readGroup}. */ @Deprecated public abstract void readUnknownGroup(final int fieldNumber, final MessageLite.Builder builder) throws IOException; /** Read an embedded message field value from the stream. */ public abstract void readMessage( final MessageLite.Builder builder, final ExtensionRegistryLite extensionRegistry) throws IOException; /** Read an embedded message field value from the stream. */ public abstract T readMessage( final Parser parser, final ExtensionRegistryLite extensionRegistry) throws IOException; /** Read a {@code bytes} field value from the stream. */ public abstract ByteString readBytes() throws IOException; /** Read a {@code bytes} field value from the stream. */ public abstract byte[] readByteArray() throws IOException; /** Read a {@code bytes} field value from the stream. */ public abstract ByteBuffer readByteBuffer() throws IOException; /** Read a {@code uint32} field value from the stream. */ public abstract int readUInt32() throws IOException; /** * Read an enum field value from the stream. Caller is responsible for converting the numeric * value to an actual enum. */ public abstract int readEnum() throws IOException; /** Read an {@code sfixed32} field value from the stream. */ public abstract int readSFixed32() throws IOException; /** Read an {@code sfixed64} field value from the stream. */ public abstract long readSFixed64() throws IOException; /** Read an {@code sint32} field value from the stream. */ public abstract int readSInt32() throws IOException; /** Read an {@code sint64} field value from the stream. */ public abstract long readSInt64() throws IOException; // ================================================================= /** Read a raw Varint from the stream. If larger than 32 bits, discard the upper bits. */ public abstract int readRawVarint32() throws IOException; /** Read a raw Varint from the stream. */ public abstract long readRawVarint64() throws IOException; /** Variant of readRawVarint64 for when uncomfortably close to the limit. */ /* Visible for testing */ abstract long readRawVarint64SlowPath() throws IOException; /** Read a 32-bit little-endian integer from the stream. */ public abstract int readRawLittleEndian32() throws IOException; /** Read a 64-bit little-endian integer from the stream. */ public abstract long readRawLittleEndian64() throws IOException; // ----------------------------------------------------------------- /** * Enables {@link ByteString} aliasing of the underlying buffer, trading off on buffer pinning for * data copies. Only valid for buffer-backed streams. */ public abstract void enableAliasing(boolean enabled); /** * Set the maximum message recursion depth. In order to prevent malicious messages from causing * stack overflows, {@code CodedInputStream} limits how deeply messages may be nested. The default * limit is 100. * * @return the old limit. */ public final int setRecursionLimit(final int limit) { if (limit < 0) { throw new IllegalArgumentException("Recursion limit cannot be negative: " + limit); } final int oldLimit = recursionLimit; recursionLimit = limit; return oldLimit; } /** * Only valid for {@link InputStream}-backed streams. * *

Set the maximum message size. In order to prevent malicious messages from exhausting memory * or causing integer overflows, {@code CodedInputStream} limits how large a message may be. The * default limit is {@code Integer.MAX_VALUE}. You should set this limit as small as you can * without harming your app's functionality. Note that size limits only apply when reading from an * {@code InputStream}, not when constructed around a raw byte array. * *

If you want to read several messages from a single CodedInputStream, you could call {@link * #resetSizeCounter()} after each one to avoid hitting the size limit. * * @return the old limit. */ public final int setSizeLimit(final int limit) { if (limit < 0) { throw new IllegalArgumentException("Size limit cannot be negative: " + limit); } final int oldLimit = sizeLimit; sizeLimit = limit; return oldLimit; } private boolean shouldDiscardUnknownFields = false; /** * Sets this {@code CodedInputStream} to discard unknown fields. Only applies to full runtime * messages; lite messages will always preserve unknowns. * *

Note calling this function alone will have NO immediate effect on the underlying input data. * The unknown fields will be discarded during parsing. This affects both Proto2 and Proto3 full * runtime. */ final void discardUnknownFields() { shouldDiscardUnknownFields = true; } /** * Reverts the unknown fields preservation behavior for Proto2 and Proto3 full runtime to their * default. */ final void unsetDiscardUnknownFields() { shouldDiscardUnknownFields = false; } /** * Whether unknown fields in this input stream should be discarded during parsing into full * runtime messages. */ final boolean shouldDiscardUnknownFields() { return shouldDiscardUnknownFields; } /** * Resets the current size counter to zero (see {@link #setSizeLimit(int)}). Only valid for {@link * InputStream}-backed streams. */ public abstract void resetSizeCounter(); /** * Sets {@code currentLimit} to (current position) + {@code byteLimit}. This is called when * descending into a length-delimited embedded message. * *

Note that {@code pushLimit()} does NOT affect how many bytes the {@code CodedInputStream} * reads from an underlying {@code InputStream} when refreshing its buffer. If you need to prevent * reading past a certain point in the underlying {@code InputStream} (e.g. because you expect it * to contain more data after the end of the message which you need to handle differently) then * you must place a wrapper around your {@code InputStream} which limits the amount of data that * can be read from it. * * @return the old limit. */ public abstract int pushLimit(int byteLimit) throws InvalidProtocolBufferException; /** * Discards the current limit, returning to the previous limit. * * @param oldLimit The old limit, as returned by {@code pushLimit}. */ public abstract void popLimit(final int oldLimit); /** * Returns the number of bytes to be read before the current limit. If no limit is set, returns * -1. */ public abstract int getBytesUntilLimit(); /** * Returns true if the stream has reached the end of the input. This is the case if either the end * of the underlying input source has been reached or if the stream has reached a limit created * using {@link #pushLimit(int)}. This function may get blocked when using StreamDecoder as it * invokes {@link StreamDecoder#tryRefillBuffer(int)} in this function which will try to read * bytes from input. */ public abstract boolean isAtEnd() throws IOException; /** * The total bytes read up to the current position. Calling {@link #resetSizeCounter()} resets * this value to zero. */ public abstract int getTotalBytesRead(); /** * Read one byte from the input. * * @throws InvalidProtocolBufferException The end of the stream or the current limit was reached. */ public abstract byte readRawByte() throws IOException; /** * Read a fixed size of bytes from the input. * * @throws InvalidProtocolBufferException The end of the stream or the current limit was reached. */ public abstract byte[] readRawBytes(final int size) throws IOException; /** * Reads and discards {@code size} bytes. * * @throws InvalidProtocolBufferException The end of the stream or the current limit was reached. */ public abstract void skipRawBytes(final int size) throws IOException; /** * Decode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers into values that can be * efficiently encoded with varint. (Otherwise, negative values must be sign-extended to 64 bits * to be varint encoded, thus always taking 10 bytes on the wire.) * * @param n An unsigned 32-bit integer, stored in a signed int because Java has no explicit * unsigned support. * @return A signed 32-bit integer. */ public static int decodeZigZag32(final int n) { return (n >>> 1) ^ -(n & 1); } /** * Decode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers into values that can be * efficiently encoded with varint. (Otherwise, negative values must be sign-extended to 64 bits * to be varint encoded, thus always taking 10 bytes on the wire.) * * @param n An unsigned 64-bit integer, stored in a signed int because Java has no explicit * unsigned support. * @return A signed 64-bit integer. */ public static long decodeZigZag64(final long n) { return (n >>> 1) ^ -(n & 1); } /** * Like {@link #readRawVarint32(InputStream)}, but expects that the caller has already read one * byte. This allows the caller to determine if EOF has been reached before attempting to read. */ public static int readRawVarint32(final int firstByte, final InputStream input) throws IOException { if ((firstByte & 0x80) == 0) { return firstByte; } int result = firstByte & 0x7f; int offset = 7; for (; offset < 32; offset += 7) { final int b = input.read(); if (b == -1) { throw InvalidProtocolBufferException.truncatedMessage(); } result |= (b & 0x7f) << offset; if ((b & 0x80) == 0) { return result; } } // Keep reading up to 64 bits. for (; offset < 64; offset += 7) { final int b = input.read(); if (b == -1) { throw InvalidProtocolBufferException.truncatedMessage(); } if ((b & 0x80) == 0) { return result; } } throw InvalidProtocolBufferException.malformedVarint(); } /** * Reads a varint from the input one byte at a time, so that it does not read any bytes after the * end of the varint. If you simply wrapped the stream in a CodedInputStream and used {@link * #readRawVarint32(InputStream)} then you would probably end up reading past the end of the * varint since CodedInputStream buffers its input. */ static int readRawVarint32(final InputStream input) throws IOException { final int firstByte = input.read(); if (firstByte == -1) { throw InvalidProtocolBufferException.truncatedMessage(); } return readRawVarint32(firstByte, input); } /** A {@link CodedInputStream} implementation that uses a backing array as the input. */ private static final class ArrayDecoder extends CodedInputStream { private final byte[] buffer; private final boolean immutable; private int limit; private int bufferSizeAfterLimit; private int pos; private int startPos; private int lastTag; private boolean enableAliasing; /** The absolute position of the end of the current message. */ private int currentLimit = Integer.MAX_VALUE; private ArrayDecoder(final byte[] buffer, final int offset, final int len, boolean immutable) { this.buffer = buffer; limit = offset + len; pos = offset; startPos = pos; this.immutable = immutable; } @Override public int readTag() throws IOException { if (isAtEnd()) { lastTag = 0; return 0; } lastTag = readRawVarint32(); if (WireFormat.getTagFieldNumber(lastTag) == 0) { // If we actually read zero (or any tag number corresponding to field // number zero), that's not a valid tag. throw InvalidProtocolBufferException.invalidTag(); } return lastTag; } @Override public void checkLastTagWas(final int value) throws InvalidProtocolBufferException { if (lastTag != value) { throw InvalidProtocolBufferException.invalidEndTag(); } } @Override public int getLastTag() { return lastTag; } @Override public boolean skipField(final int tag) throws IOException { switch (WireFormat.getTagWireType(tag)) { case WireFormat.WIRETYPE_VARINT: skipRawVarint(); return true; case WireFormat.WIRETYPE_FIXED64: skipRawBytes(FIXED64_SIZE); return true; case WireFormat.WIRETYPE_LENGTH_DELIMITED: skipRawBytes(readRawVarint32()); return true; case WireFormat.WIRETYPE_START_GROUP: skipMessage(); checkLastTagWas( WireFormat.makeTag(WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP)); return true; case WireFormat.WIRETYPE_END_GROUP: return false; case WireFormat.WIRETYPE_FIXED32: skipRawBytes(FIXED32_SIZE); return true; default: throw InvalidProtocolBufferException.invalidWireType(); } } @Override public boolean skipField(final int tag, final CodedOutputStream output) throws IOException { switch (WireFormat.getTagWireType(tag)) { case WireFormat.WIRETYPE_VARINT: { long value = readInt64(); output.writeUInt32NoTag(tag); output.writeUInt64NoTag(value); return true; } case WireFormat.WIRETYPE_FIXED64: { long value = readRawLittleEndian64(); output.writeUInt32NoTag(tag); output.writeFixed64NoTag(value); return true; } case WireFormat.WIRETYPE_LENGTH_DELIMITED: { ByteString value = readBytes(); output.writeUInt32NoTag(tag); output.writeBytesNoTag(value); return true; } case WireFormat.WIRETYPE_START_GROUP: { output.writeUInt32NoTag(tag); skipMessage(output); int endtag = WireFormat.makeTag( WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP); checkLastTagWas(endtag); output.writeUInt32NoTag(endtag); return true; } case WireFormat.WIRETYPE_END_GROUP: { return false; } case WireFormat.WIRETYPE_FIXED32: { int value = readRawLittleEndian32(); output.writeUInt32NoTag(tag); output.writeFixed32NoTag(value); return true; } default: throw InvalidProtocolBufferException.invalidWireType(); } } // ----------------------------------------------------------------- @Override public double readDouble() throws IOException { return Double.longBitsToDouble(readRawLittleEndian64()); } @Override public float readFloat() throws IOException { return Float.intBitsToFloat(readRawLittleEndian32()); } @Override public long readUInt64() throws IOException { return readRawVarint64(); } @Override public long readInt64() throws IOException { return readRawVarint64(); } @Override public int readInt32() throws IOException { return readRawVarint32(); } @Override public long readFixed64() throws IOException { return readRawLittleEndian64(); } @Override public int readFixed32() throws IOException { return readRawLittleEndian32(); } @Override public boolean readBool() throws IOException { return readRawVarint64() != 0; } @Override public String readString() throws IOException { final int size = readRawVarint32(); if (size > 0 && size <= (limit - pos)) { // Fast path: We already have the bytes in a contiguous buffer, so // just copy directly from it. final String result = new String(buffer, pos, size, UTF_8); pos += size; return result; } if (size == 0) { return ""; } if (size < 0) { throw InvalidProtocolBufferException.negativeSize(); } throw InvalidProtocolBufferException.truncatedMessage(); } @Override public String readStringRequireUtf8() throws IOException { final int size = readRawVarint32(); if (size > 0 && size <= (limit - pos)) { String result = Utf8.decodeUtf8(buffer, pos, size); pos += size; return result; } if (size == 0) { return ""; } if (size <= 0) { throw InvalidProtocolBufferException.negativeSize(); } throw InvalidProtocolBufferException.truncatedMessage(); } @Override public void readGroup( final int fieldNumber, final MessageLite.Builder builder, final ExtensionRegistryLite extensionRegistry) throws IOException { checkRecursionLimit(); ++recursionDepth; builder.mergeFrom(this, extensionRegistry); checkLastTagWas(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); --recursionDepth; } @Override public T readGroup( final int fieldNumber, final Parser parser, final ExtensionRegistryLite extensionRegistry) throws IOException { checkRecursionLimit(); ++recursionDepth; T result = parser.parsePartialFrom(this, extensionRegistry); checkLastTagWas(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); --recursionDepth; return result; } @Deprecated @Override public void readUnknownGroup(final int fieldNumber, final MessageLite.Builder builder) throws IOException { readGroup(fieldNumber, builder, ExtensionRegistryLite.getEmptyRegistry()); } @Override public void readMessage( final MessageLite.Builder builder, final ExtensionRegistryLite extensionRegistry) throws IOException { final int length = readRawVarint32(); checkRecursionLimit(); final int oldLimit = pushLimit(length); ++recursionDepth; builder.mergeFrom(this, extensionRegistry); checkLastTagWas(0); --recursionDepth; if (getBytesUntilLimit() != 0) { throw InvalidProtocolBufferException.truncatedMessage(); } popLimit(oldLimit); } @Override public T readMessage( final Parser parser, final ExtensionRegistryLite extensionRegistry) throws IOException { int length = readRawVarint32(); checkRecursionLimit(); final int oldLimit = pushLimit(length); ++recursionDepth; T result = parser.parsePartialFrom(this, extensionRegistry); checkLastTagWas(0); --recursionDepth; if (getBytesUntilLimit() != 0) { throw InvalidProtocolBufferException.truncatedMessage(); } popLimit(oldLimit); return result; } @Override public ByteString readBytes() throws IOException { final int size = readRawVarint32(); if (size > 0 && size <= (limit - pos)) { // Fast path: We already have the bytes in a contiguous buffer, so // just copy directly from it. final ByteString result = immutable && enableAliasing ? ByteString.wrap(buffer, pos, size) : ByteString.copyFrom(buffer, pos, size); pos += size; return result; } if (size == 0) { return ByteString.EMPTY; } // Slow path: Build a byte array first then copy it. return ByteString.wrap(readRawBytes(size)); } @Override public byte[] readByteArray() throws IOException { final int size = readRawVarint32(); return readRawBytes(size); } @Override public ByteBuffer readByteBuffer() throws IOException { final int size = readRawVarint32(); if (size > 0 && size <= (limit - pos)) { // Fast path: We already have the bytes in a contiguous buffer. // When aliasing is enabled, we can return a ByteBuffer pointing directly // into the underlying byte array without copy if the CodedInputStream is // constructed from a byte array. If aliasing is disabled or the input is // from an InputStream or ByteString, we have to make a copy of the bytes. ByteBuffer result = !immutable && enableAliasing ? ByteBuffer.wrap(buffer, pos, size).slice() : ByteBuffer.wrap(Arrays.copyOfRange(buffer, pos, pos + size)); pos += size; // TODO: Investigate making the ByteBuffer be made read-only return result; } if (size == 0) { return EMPTY_BYTE_BUFFER; } if (size < 0) { throw InvalidProtocolBufferException.negativeSize(); } throw InvalidProtocolBufferException.truncatedMessage(); } @Override public int readUInt32() throws IOException { return readRawVarint32(); } @Override public int readEnum() throws IOException { return readRawVarint32(); } @Override public int readSFixed32() throws IOException { return readRawLittleEndian32(); } @Override public long readSFixed64() throws IOException { return readRawLittleEndian64(); } @Override public int readSInt32() throws IOException { return decodeZigZag32(readRawVarint32()); } @Override public long readSInt64() throws IOException { return decodeZigZag64(readRawVarint64()); } // ================================================================= @Override public int readRawVarint32() throws IOException { // See implementation notes for readRawVarint64 fastpath: { int tempPos = pos; if (limit == tempPos) { break fastpath; } final byte[] buffer = this.buffer; int x; if ((x = buffer[tempPos++]) >= 0) { pos = tempPos; return x; } else if (limit - tempPos < 9) { break fastpath; } else if ((x ^= (buffer[tempPos++] << 7)) < 0) { x ^= (~0 << 7); } else if ((x ^= (buffer[tempPos++] << 14)) >= 0) { x ^= (~0 << 7) ^ (~0 << 14); } else if ((x ^= (buffer[tempPos++] << 21)) < 0) { x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21); } else { int y = buffer[tempPos++]; x ^= y << 28; x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21) ^ (~0 << 28); if (y < 0 && buffer[tempPos++] < 0 && buffer[tempPos++] < 0 && buffer[tempPos++] < 0 && buffer[tempPos++] < 0 && buffer[tempPos++] < 0) { break fastpath; // Will throw malformedVarint() } } pos = tempPos; return x; } return (int) readRawVarint64SlowPath(); } private void skipRawVarint() throws IOException { if (limit - pos >= MAX_VARINT_SIZE) { skipRawVarintFastPath(); } else { skipRawVarintSlowPath(); } } private void skipRawVarintFastPath() throws IOException { for (int i = 0; i < MAX_VARINT_SIZE; i++) { if (buffer[pos++] >= 0) { return; } } throw InvalidProtocolBufferException.malformedVarint(); } private void skipRawVarintSlowPath() throws IOException { for (int i = 0; i < MAX_VARINT_SIZE; i++) { if (readRawByte() >= 0) { return; } } throw InvalidProtocolBufferException.malformedVarint(); } @Override public long readRawVarint64() 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. fastpath: { int tempPos = pos; if (limit == tempPos) { break fastpath; } final byte[] buffer = this.buffer; long x; int y; if ((y = buffer[tempPos++]) >= 0) { pos = tempPos; return y; } else if (limit - tempPos < 9) { break fastpath; } else if ((y ^= (buffer[tempPos++] << 7)) < 0) { x = y ^ (~0 << 7); } else if ((y ^= (buffer[tempPos++] << 14)) >= 0) { x = y ^ ((~0 << 7) ^ (~0 << 14)); } else if ((y ^= (buffer[tempPos++] << 21)) < 0) { x = y ^ ((~0 << 7) ^ (~0 << 14) ^ (~0 << 21)); } else if ((x = y ^ ((long) buffer[tempPos++] << 28)) >= 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28); } else if ((x ^= ((long) buffer[tempPos++] << 35)) < 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35); } else if ((x ^= ((long) buffer[tempPos++] << 42)) >= 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42); } else if ((x ^= ((long) buffer[tempPos++] << 49)) < 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42) ^ (~0L << 49); } else { x ^= ((long) buffer[tempPos++] << 56); x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42) ^ (~0L << 49) ^ (~0L << 56); if (x < 0L) { if (buffer[tempPos++] < 0L) { break fastpath; // Will throw malformedVarint() } } } pos = tempPos; return x; } return readRawVarint64SlowPath(); } @Override long readRawVarint64SlowPath() throws IOException { long result = 0; for (int shift = 0; shift < 64; shift += 7) { final byte b = readRawByte(); result |= (long) (b & 0x7F) << shift; if ((b & 0x80) == 0) { return result; } } throw InvalidProtocolBufferException.malformedVarint(); } @Override public int readRawLittleEndian32() throws IOException { int tempPos = pos; if (limit - tempPos < FIXED32_SIZE) { throw InvalidProtocolBufferException.truncatedMessage(); } final byte[] buffer = this.buffer; pos = tempPos + FIXED32_SIZE; return ((buffer[tempPos] & 0xff) | ((buffer[tempPos + 1] & 0xff) << 8) | ((buffer[tempPos + 2] & 0xff) << 16) | ((buffer[tempPos + 3] & 0xff) << 24)); } @Override public long readRawLittleEndian64() throws IOException { int tempPos = pos; if (limit - tempPos < FIXED64_SIZE) { throw InvalidProtocolBufferException.truncatedMessage(); } final byte[] buffer = this.buffer; pos = tempPos + FIXED64_SIZE; return ((buffer[tempPos] & 0xffL) | ((buffer[tempPos + 1] & 0xffL) << 8) | ((buffer[tempPos + 2] & 0xffL) << 16) | ((buffer[tempPos + 3] & 0xffL) << 24) | ((buffer[tempPos + 4] & 0xffL) << 32) | ((buffer[tempPos + 5] & 0xffL) << 40) | ((buffer[tempPos + 6] & 0xffL) << 48) | ((buffer[tempPos + 7] & 0xffL) << 56)); } @Override public void enableAliasing(boolean enabled) { this.enableAliasing = enabled; } @Override public void resetSizeCounter() { startPos = pos; } @Override public int pushLimit(int byteLimit) throws InvalidProtocolBufferException { if (byteLimit < 0) { throw InvalidProtocolBufferException.negativeSize(); } byteLimit += getTotalBytesRead(); if (byteLimit < 0) { throw InvalidProtocolBufferException.parseFailure(); } final int oldLimit = currentLimit; if (byteLimit > oldLimit) { throw InvalidProtocolBufferException.truncatedMessage(); } currentLimit = byteLimit; recomputeBufferSizeAfterLimit(); return oldLimit; } private void recomputeBufferSizeAfterLimit() { limit += bufferSizeAfterLimit; final int bufferEnd = limit - startPos; if (bufferEnd > currentLimit) { // Limit is in current buffer. bufferSizeAfterLimit = bufferEnd - currentLimit; limit -= bufferSizeAfterLimit; } else { bufferSizeAfterLimit = 0; } } @Override public void popLimit(final int oldLimit) { currentLimit = oldLimit; recomputeBufferSizeAfterLimit(); } @Override public int getBytesUntilLimit() { if (currentLimit == Integer.MAX_VALUE) { return -1; } return currentLimit - getTotalBytesRead(); } @Override public boolean isAtEnd() throws IOException { return pos == limit; } @Override public int getTotalBytesRead() { return pos - startPos; } @Override public byte readRawByte() throws IOException { if (pos == limit) { throw InvalidProtocolBufferException.truncatedMessage(); } return buffer[pos++]; } @Override public byte[] readRawBytes(final int length) throws IOException { if (length > 0 && length <= (limit - pos)) { final int tempPos = pos; pos += length; return Arrays.copyOfRange(buffer, tempPos, pos); } if (length <= 0) { if (length == 0) { return Internal.EMPTY_BYTE_ARRAY; } else { throw InvalidProtocolBufferException.negativeSize(); } } throw InvalidProtocolBufferException.truncatedMessage(); } @Override public void skipRawBytes(final int length) throws IOException { if (length >= 0 && length <= (limit - pos)) { // We have all the bytes we need already. pos += length; return; } if (length < 0) { throw InvalidProtocolBufferException.negativeSize(); } throw InvalidProtocolBufferException.truncatedMessage(); } } /** * A {@link CodedInputStream} implementation that uses a backing direct ByteBuffer as the input. * Requires the use of {@code sun.misc.Unsafe} to perform fast reads on the buffer. */ private static final class UnsafeDirectNioDecoder extends CodedInputStream { /** The direct buffer that is backing this stream. */ private final ByteBuffer buffer; /** * If {@code true}, indicates that the buffer is backing a {@link ByteString} and is therefore * considered to be an immutable input source. */ private final boolean immutable; /** The unsafe address of the content of {@link #buffer}. */ private final long address; /** The unsafe address of the current read limit of the buffer. */ private long limit; /** The unsafe address of the current read position of the buffer. */ private long pos; /** The unsafe address of the starting read position. */ private long startPos; /** The amount of available data in the buffer beyond {@link #limit}. */ private int bufferSizeAfterLimit; /** The last tag that was read from this stream. */ private int lastTag; /** * If {@code true}, indicates that calls to read {@link ByteString} or {@code byte[]} * may return slices of the underlying buffer, rather than copies. */ private boolean enableAliasing; /** The absolute position of the end of the current message. */ private int currentLimit = Integer.MAX_VALUE; static boolean isSupported() { return UnsafeUtil.hasUnsafeByteBufferOperations(); } private UnsafeDirectNioDecoder(ByteBuffer buffer, boolean immutable) { this.buffer = buffer; address = UnsafeUtil.addressOffset(buffer); limit = address + buffer.limit(); pos = address + buffer.position(); startPos = pos; this.immutable = immutable; } @Override public int readTag() throws IOException { if (isAtEnd()) { lastTag = 0; return 0; } lastTag = readRawVarint32(); if (WireFormat.getTagFieldNumber(lastTag) == 0) { // If we actually read zero (or any tag number corresponding to field // number zero), that's not a valid tag. throw InvalidProtocolBufferException.invalidTag(); } return lastTag; } @Override public void checkLastTagWas(final int value) throws InvalidProtocolBufferException { if (lastTag != value) { throw InvalidProtocolBufferException.invalidEndTag(); } } @Override public int getLastTag() { return lastTag; } @Override public boolean skipField(final int tag) throws IOException { switch (WireFormat.getTagWireType(tag)) { case WireFormat.WIRETYPE_VARINT: skipRawVarint(); return true; case WireFormat.WIRETYPE_FIXED64: skipRawBytes(FIXED64_SIZE); return true; case WireFormat.WIRETYPE_LENGTH_DELIMITED: skipRawBytes(readRawVarint32()); return true; case WireFormat.WIRETYPE_START_GROUP: skipMessage(); checkLastTagWas( WireFormat.makeTag(WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP)); return true; case WireFormat.WIRETYPE_END_GROUP: return false; case WireFormat.WIRETYPE_FIXED32: skipRawBytes(FIXED32_SIZE); return true; default: throw InvalidProtocolBufferException.invalidWireType(); } } @Override public boolean skipField(final int tag, final CodedOutputStream output) throws IOException { switch (WireFormat.getTagWireType(tag)) { case WireFormat.WIRETYPE_VARINT: { long value = readInt64(); output.writeUInt32NoTag(tag); output.writeUInt64NoTag(value); return true; } case WireFormat.WIRETYPE_FIXED64: { long value = readRawLittleEndian64(); output.writeUInt32NoTag(tag); output.writeFixed64NoTag(value); return true; } case WireFormat.WIRETYPE_LENGTH_DELIMITED: { ByteString value = readBytes(); output.writeUInt32NoTag(tag); output.writeBytesNoTag(value); return true; } case WireFormat.WIRETYPE_START_GROUP: { output.writeUInt32NoTag(tag); skipMessage(output); int endtag = WireFormat.makeTag( WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP); checkLastTagWas(endtag); output.writeUInt32NoTag(endtag); return true; } case WireFormat.WIRETYPE_END_GROUP: { return false; } case WireFormat.WIRETYPE_FIXED32: { int value = readRawLittleEndian32(); output.writeUInt32NoTag(tag); output.writeFixed32NoTag(value); return true; } default: throw InvalidProtocolBufferException.invalidWireType(); } } // ----------------------------------------------------------------- @Override public double readDouble() throws IOException { return Double.longBitsToDouble(readRawLittleEndian64()); } @Override public float readFloat() throws IOException { return Float.intBitsToFloat(readRawLittleEndian32()); } @Override public long readUInt64() throws IOException { return readRawVarint64(); } @Override public long readInt64() throws IOException { return readRawVarint64(); } @Override public int readInt32() throws IOException { return readRawVarint32(); } @Override public long readFixed64() throws IOException { return readRawLittleEndian64(); } @Override public int readFixed32() throws IOException { return readRawLittleEndian32(); } @Override public boolean readBool() throws IOException { return readRawVarint64() != 0; } @Override public String readString() throws IOException { final int size = readRawVarint32(); if (size > 0 && size <= remaining()) { // TODO: Is there a way to avoid this copy? // TODO: It might be possible to share the optimized loop with // readStringRequireUtf8 by implementing Java replacement logic there. // The same as readBytes' logic byte[] bytes = new byte[size]; UnsafeUtil.copyMemory(pos, bytes, 0, size); String result = new String(bytes, UTF_8); pos += size; return result; } if (size == 0) { return ""; } if (size < 0) { throw InvalidProtocolBufferException.negativeSize(); } throw InvalidProtocolBufferException.truncatedMessage(); } @Override public String readStringRequireUtf8() throws IOException { final int size = readRawVarint32(); if (size > 0 && size <= remaining()) { final int bufferPos = bufferPos(pos); String result = Utf8.decodeUtf8(buffer, bufferPos, size); pos += size; return result; } if (size == 0) { return ""; } if (size <= 0) { throw InvalidProtocolBufferException.negativeSize(); } throw InvalidProtocolBufferException.truncatedMessage(); } @Override public void readGroup( final int fieldNumber, final MessageLite.Builder builder, final ExtensionRegistryLite extensionRegistry) throws IOException { checkRecursionLimit(); ++recursionDepth; builder.mergeFrom(this, extensionRegistry); checkLastTagWas(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); --recursionDepth; } @Override public T readGroup( final int fieldNumber, final Parser parser, final ExtensionRegistryLite extensionRegistry) throws IOException { checkRecursionLimit(); ++recursionDepth; T result = parser.parsePartialFrom(this, extensionRegistry); checkLastTagWas(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); --recursionDepth; return result; } @Deprecated @Override public void readUnknownGroup(final int fieldNumber, final MessageLite.Builder builder) throws IOException { readGroup(fieldNumber, builder, ExtensionRegistryLite.getEmptyRegistry()); } @Override public void readMessage( final MessageLite.Builder builder, final ExtensionRegistryLite extensionRegistry) throws IOException { final int length = readRawVarint32(); checkRecursionLimit(); final int oldLimit = pushLimit(length); ++recursionDepth; builder.mergeFrom(this, extensionRegistry); checkLastTagWas(0); --recursionDepth; if (getBytesUntilLimit() != 0) { throw InvalidProtocolBufferException.truncatedMessage(); } popLimit(oldLimit); } @Override public T readMessage( final Parser parser, final ExtensionRegistryLite extensionRegistry) throws IOException { int length = readRawVarint32(); checkRecursionLimit(); final int oldLimit = pushLimit(length); ++recursionDepth; T result = parser.parsePartialFrom(this, extensionRegistry); checkLastTagWas(0); --recursionDepth; if (getBytesUntilLimit() != 0) { throw InvalidProtocolBufferException.truncatedMessage(); } popLimit(oldLimit); return result; } @Override public ByteString readBytes() throws IOException { final int size = readRawVarint32(); if (size > 0 && size <= remaining()) { if (immutable && enableAliasing) { final ByteBuffer result = slice(pos, pos + size); pos += size; return ByteString.wrap(result); } else { // Use UnsafeUtil to copy the memory to bytes instead of using ByteBuffer ways. byte[] bytes = new byte[size]; UnsafeUtil.copyMemory(pos, bytes, 0, size); pos += size; return ByteString.wrap(bytes); } } if (size == 0) { return ByteString.EMPTY; } if (size < 0) { throw InvalidProtocolBufferException.negativeSize(); } throw InvalidProtocolBufferException.truncatedMessage(); } @Override public byte[] readByteArray() throws IOException { return readRawBytes(readRawVarint32()); } @Override public ByteBuffer readByteBuffer() throws IOException { final int size = readRawVarint32(); if (size > 0 && size <= remaining()) { // "Immutable" implies that buffer is backing a ByteString. // Disallow slicing in this case to prevent the caller from modifying the contents // of the ByteString. if (!immutable && enableAliasing) { final ByteBuffer result = slice(pos, pos + size); pos += size; return result; } else { // The same as readBytes' logic byte[] bytes = new byte[size]; UnsafeUtil.copyMemory(pos, bytes, 0, size); pos += size; return ByteBuffer.wrap(bytes); } // TODO: Investigate making the ByteBuffer be made read-only } if (size == 0) { return EMPTY_BYTE_BUFFER; } if (size < 0) { throw InvalidProtocolBufferException.negativeSize(); } throw InvalidProtocolBufferException.truncatedMessage(); } @Override public int readUInt32() throws IOException { return readRawVarint32(); } @Override public int readEnum() throws IOException { return readRawVarint32(); } @Override public int readSFixed32() throws IOException { return readRawLittleEndian32(); } @Override public long readSFixed64() throws IOException { return readRawLittleEndian64(); } @Override public int readSInt32() throws IOException { return decodeZigZag32(readRawVarint32()); } @Override public long readSInt64() throws IOException { return decodeZigZag64(readRawVarint64()); } // ================================================================= @Override public int readRawVarint32() throws IOException { // See implementation notes for readRawVarint64 fastpath: { long tempPos = pos; if (limit == tempPos) { break fastpath; } int x; if ((x = UnsafeUtil.getByte(tempPos++)) >= 0) { pos = tempPos; return x; } else if (limit - tempPos < 9) { break fastpath; } else if ((x ^= (UnsafeUtil.getByte(tempPos++) << 7)) < 0) { x ^= (~0 << 7); } else if ((x ^= (UnsafeUtil.getByte(tempPos++) << 14)) >= 0) { x ^= (~0 << 7) ^ (~0 << 14); } else if ((x ^= (UnsafeUtil.getByte(tempPos++) << 21)) < 0) { x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21); } else { int y = UnsafeUtil.getByte(tempPos++); x ^= y << 28; x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21) ^ (~0 << 28); if (y < 0 && UnsafeUtil.getByte(tempPos++) < 0 && UnsafeUtil.getByte(tempPos++) < 0 && UnsafeUtil.getByte(tempPos++) < 0 && UnsafeUtil.getByte(tempPos++) < 0 && UnsafeUtil.getByte(tempPos++) < 0) { break fastpath; // Will throw malformedVarint() } } pos = tempPos; return x; } return (int) readRawVarint64SlowPath(); } private void skipRawVarint() throws IOException { if (remaining() >= MAX_VARINT_SIZE) { skipRawVarintFastPath(); } else { skipRawVarintSlowPath(); } } private void skipRawVarintFastPath() throws IOException { for (int i = 0; i < MAX_VARINT_SIZE; i++) { if (UnsafeUtil.getByte(pos++) >= 0) { return; } } throw InvalidProtocolBufferException.malformedVarint(); } private void skipRawVarintSlowPath() throws IOException { for (int i = 0; i < MAX_VARINT_SIZE; i++) { if (readRawByte() >= 0) { return; } } throw InvalidProtocolBufferException.malformedVarint(); } @Override public long readRawVarint64() 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. fastpath: { long tempPos = pos; if (limit == tempPos) { break fastpath; } long x; int y; if ((y = UnsafeUtil.getByte(tempPos++)) >= 0) { pos = tempPos; return y; } else if (limit - tempPos < 9) { break fastpath; } else if ((y ^= (UnsafeUtil.getByte(tempPos++) << 7)) < 0) { x = y ^ (~0 << 7); } else if ((y ^= (UnsafeUtil.getByte(tempPos++) << 14)) >= 0) { x = y ^ ((~0 << 7) ^ (~0 << 14)); } else if ((y ^= (UnsafeUtil.getByte(tempPos++) << 21)) < 0) { x = y ^ ((~0 << 7) ^ (~0 << 14) ^ (~0 << 21)); } else if ((x = y ^ ((long) UnsafeUtil.getByte(tempPos++) << 28)) >= 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28); } else if ((x ^= ((long) UnsafeUtil.getByte(tempPos++) << 35)) < 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35); } else if ((x ^= ((long) UnsafeUtil.getByte(tempPos++) << 42)) >= 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42); } else if ((x ^= ((long) UnsafeUtil.getByte(tempPos++) << 49)) < 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42) ^ (~0L << 49); } else { x ^= ((long) UnsafeUtil.getByte(tempPos++) << 56); x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42) ^ (~0L << 49) ^ (~0L << 56); if (x < 0L) { if (UnsafeUtil.getByte(tempPos++) < 0L) { break fastpath; // Will throw malformedVarint() } } } pos = tempPos; return x; } return readRawVarint64SlowPath(); } @Override long readRawVarint64SlowPath() throws IOException { long result = 0; for (int shift = 0; shift < 64; shift += 7) { final byte b = readRawByte(); result |= (long) (b & 0x7F) << shift; if ((b & 0x80) == 0) { return result; } } throw InvalidProtocolBufferException.malformedVarint(); } @Override public int readRawLittleEndian32() throws IOException { long tempPos = pos; if (limit - tempPos < FIXED32_SIZE) { throw InvalidProtocolBufferException.truncatedMessage(); } pos = tempPos + FIXED32_SIZE; return ((UnsafeUtil.getByte(tempPos) & 0xff) | ((UnsafeUtil.getByte(tempPos + 1) & 0xff) << 8) | ((UnsafeUtil.getByte(tempPos + 2) & 0xff) << 16) | ((UnsafeUtil.getByte(tempPos + 3) & 0xff) << 24)); } @Override public long readRawLittleEndian64() throws IOException { long tempPos = pos; if (limit - tempPos < FIXED64_SIZE) { throw InvalidProtocolBufferException.truncatedMessage(); } pos = tempPos + FIXED64_SIZE; return ((UnsafeUtil.getByte(tempPos) & 0xffL) | ((UnsafeUtil.getByte(tempPos + 1) & 0xffL) << 8) | ((UnsafeUtil.getByte(tempPos + 2) & 0xffL) << 16) | ((UnsafeUtil.getByte(tempPos + 3) & 0xffL) << 24) | ((UnsafeUtil.getByte(tempPos + 4) & 0xffL) << 32) | ((UnsafeUtil.getByte(tempPos + 5) & 0xffL) << 40) | ((UnsafeUtil.getByte(tempPos + 6) & 0xffL) << 48) | ((UnsafeUtil.getByte(tempPos + 7) & 0xffL) << 56)); } @Override public void enableAliasing(boolean enabled) { this.enableAliasing = enabled; } @Override public void resetSizeCounter() { startPos = pos; } @Override public int pushLimit(int byteLimit) throws InvalidProtocolBufferException { if (byteLimit < 0) { throw InvalidProtocolBufferException.negativeSize(); } byteLimit += getTotalBytesRead(); final int oldLimit = currentLimit; if (byteLimit > oldLimit) { throw InvalidProtocolBufferException.truncatedMessage(); } currentLimit = byteLimit; recomputeBufferSizeAfterLimit(); return oldLimit; } @Override public void popLimit(final int oldLimit) { currentLimit = oldLimit; recomputeBufferSizeAfterLimit(); } @Override public int getBytesUntilLimit() { if (currentLimit == Integer.MAX_VALUE) { return -1; } return currentLimit - getTotalBytesRead(); } @Override public boolean isAtEnd() throws IOException { return pos == limit; } @Override public int getTotalBytesRead() { return (int) (pos - startPos); } @Override public byte readRawByte() throws IOException { if (pos == limit) { throw InvalidProtocolBufferException.truncatedMessage(); } return UnsafeUtil.getByte(pos++); } @Override public byte[] readRawBytes(final int length) throws IOException { if (length >= 0 && length <= remaining()) { byte[] bytes = new byte[length]; slice(pos, pos + length).get(bytes); pos += length; return bytes; } if (length <= 0) { if (length == 0) { return EMPTY_BYTE_ARRAY; } else { throw InvalidProtocolBufferException.negativeSize(); } } throw InvalidProtocolBufferException.truncatedMessage(); } @Override public void skipRawBytes(final int length) throws IOException { if (length >= 0 && length <= remaining()) { // We have all the bytes we need already. pos += length; return; } if (length < 0) { throw InvalidProtocolBufferException.negativeSize(); } throw InvalidProtocolBufferException.truncatedMessage(); } private void recomputeBufferSizeAfterLimit() { limit += bufferSizeAfterLimit; final int bufferEnd = (int) (limit - startPos); if (bufferEnd > currentLimit) { // Limit is in current buffer. bufferSizeAfterLimit = bufferEnd - currentLimit; limit -= bufferSizeAfterLimit; } else { bufferSizeAfterLimit = 0; } } private int remaining() { return (int) (limit - pos); } private int bufferPos(long pos) { return (int) (pos - address); } private ByteBuffer slice(long begin, long end) throws IOException { int prevPos = buffer.position(); int prevLimit = buffer.limit(); // View ByteBuffer as Buffer to avoid cross-Java version issues. // See https://issues.apache.org/jira/browse/MRESOLVER-85 Buffer asBuffer = buffer; try { asBuffer.position(bufferPos(begin)); asBuffer.limit(bufferPos(end)); return buffer.slice(); } catch (IllegalArgumentException e) { InvalidProtocolBufferException ex = InvalidProtocolBufferException.truncatedMessage(); ex.initCause(e); throw ex; } finally { asBuffer.position(prevPos); asBuffer.limit(prevLimit); } } } /** * Implementation of {@link CodedInputStream} that uses an {@link InputStream} as the data source. */ private static final class StreamDecoder extends CodedInputStream { private final InputStream input; private final byte[] buffer; /** bufferSize represents how many bytes are currently filled in the buffer */ private int bufferSize; private int bufferSizeAfterLimit; private int pos; private int lastTag; /** * The total number of bytes read before the current buffer. The total bytes read up to the * current position can be computed as {@code totalBytesRetired + pos}. This value may be * negative if reading started in the middle of the current buffer (e.g. if the constructor that * takes a byte array and an offset was used). */ private int totalBytesRetired; /** The absolute position of the end of the current message. */ private int currentLimit = Integer.MAX_VALUE; private StreamDecoder(final InputStream input, int bufferSize) { checkNotNull(input, "input"); this.input = input; this.buffer = new byte[bufferSize]; this.bufferSize = 0; pos = 0; totalBytesRetired = 0; } /* * The following wrapper methods exist so that InvalidProtocolBufferExceptions thrown by the * InputStream can be differentiated from ones thrown by CodedInputStream itself. Each call to * an InputStream method that can throw IOException must be wrapped like this. We do this * because we sometimes need to modify IPBE instances after they are thrown far away from where * they are thrown (ex. to add unfinished messages) and we use this signal elsewhere in the * exception catch chain to know when to perform these operations directly or to wrap the * exception in their own IPBE so the extra information can be communicated without trampling * downstream information. */ private static int read(InputStream input, byte[] data, int offset, int length) throws IOException { try { return input.read(data, offset, length); } catch (InvalidProtocolBufferException e) { e.setThrownFromInputStream(); throw e; } } private static long skip(InputStream input, long length) throws IOException { try { return input.skip(length); } catch (InvalidProtocolBufferException e) { e.setThrownFromInputStream(); throw e; } } private static int available(InputStream input) throws IOException { try { return input.available(); } catch (InvalidProtocolBufferException e) { e.setThrownFromInputStream(); throw e; } } @Override public int readTag() throws IOException { if (isAtEnd()) { lastTag = 0; return 0; } lastTag = readRawVarint32(); if (WireFormat.getTagFieldNumber(lastTag) == 0) { // If we actually read zero (or any tag number corresponding to field // number zero), that's not a valid tag. throw InvalidProtocolBufferException.invalidTag(); } return lastTag; } @Override public void checkLastTagWas(final int value) throws InvalidProtocolBufferException { if (lastTag != value) { throw InvalidProtocolBufferException.invalidEndTag(); } } @Override public int getLastTag() { return lastTag; } @Override public boolean skipField(final int tag) throws IOException { switch (WireFormat.getTagWireType(tag)) { case WireFormat.WIRETYPE_VARINT: skipRawVarint(); return true; case WireFormat.WIRETYPE_FIXED64: skipRawBytes(FIXED64_SIZE); return true; case WireFormat.WIRETYPE_LENGTH_DELIMITED: skipRawBytes(readRawVarint32()); return true; case WireFormat.WIRETYPE_START_GROUP: skipMessage(); checkLastTagWas( WireFormat.makeTag(WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP)); return true; case WireFormat.WIRETYPE_END_GROUP: return false; case WireFormat.WIRETYPE_FIXED32: skipRawBytes(FIXED32_SIZE); return true; default: throw InvalidProtocolBufferException.invalidWireType(); } } @Override public boolean skipField(final int tag, final CodedOutputStream output) throws IOException { switch (WireFormat.getTagWireType(tag)) { case WireFormat.WIRETYPE_VARINT: { long value = readInt64(); output.writeUInt32NoTag(tag); output.writeUInt64NoTag(value); return true; } case WireFormat.WIRETYPE_FIXED64: { long value = readRawLittleEndian64(); output.writeUInt32NoTag(tag); output.writeFixed64NoTag(value); return true; } case WireFormat.WIRETYPE_LENGTH_DELIMITED: { ByteString value = readBytes(); output.writeUInt32NoTag(tag); output.writeBytesNoTag(value); return true; } case WireFormat.WIRETYPE_START_GROUP: { output.writeUInt32NoTag(tag); skipMessage(output); int endtag = WireFormat.makeTag( WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP); checkLastTagWas(endtag); output.writeUInt32NoTag(endtag); return true; } case WireFormat.WIRETYPE_END_GROUP: { return false; } case WireFormat.WIRETYPE_FIXED32: { int value = readRawLittleEndian32(); output.writeUInt32NoTag(tag); output.writeFixed32NoTag(value); return true; } default: throw InvalidProtocolBufferException.invalidWireType(); } } /** Collects the bytes skipped and returns the data in a ByteBuffer. */ private class SkippedDataSink implements RefillCallback { private int lastPos = pos; private ByteArrayOutputStream byteArrayStream; @Override public void onRefill() { if (byteArrayStream == null) { byteArrayStream = new ByteArrayOutputStream(); } byteArrayStream.write(buffer, lastPos, pos - lastPos); lastPos = 0; } /** Gets skipped data in a ByteBuffer. This method should only be called once. */ ByteBuffer getSkippedData() { if (byteArrayStream == null) { return ByteBuffer.wrap(buffer, lastPos, pos - lastPos); } else { byteArrayStream.write(buffer, lastPos, pos); return ByteBuffer.wrap(byteArrayStream.toByteArray()); } } } // ----------------------------------------------------------------- @Override public double readDouble() throws IOException { return Double.longBitsToDouble(readRawLittleEndian64()); } @Override public float readFloat() throws IOException { return Float.intBitsToFloat(readRawLittleEndian32()); } @Override public long readUInt64() throws IOException { return readRawVarint64(); } @Override public long readInt64() throws IOException { return readRawVarint64(); } @Override public int readInt32() throws IOException { return readRawVarint32(); } @Override public long readFixed64() throws IOException { return readRawLittleEndian64(); } @Override public int readFixed32() throws IOException { return readRawLittleEndian32(); } @Override public boolean readBool() throws IOException { return readRawVarint64() != 0; } @Override public String readString() throws IOException { final int size = readRawVarint32(); if (size > 0 && size <= (bufferSize - pos)) { // Fast path: We already have the bytes in a contiguous buffer, so // just copy directly from it. final String result = new String(buffer, pos, size, UTF_8); pos += size; return result; } if (size == 0) { return ""; } if (size < 0) { throw InvalidProtocolBufferException.negativeSize(); } if (size <= bufferSize) { refillBuffer(size); String result = new String(buffer, pos, size, UTF_8); pos += size; return result; } // Slow path: Build a byte array first then copy it. return new String(readRawBytesSlowPath(size, /* ensureNoLeakedReferences= */ false), UTF_8); } @Override public String readStringRequireUtf8() throws IOException { final int size = readRawVarint32(); final byte[] bytes; final int oldPos = pos; final int tempPos; if (size <= (bufferSize - oldPos) && size > 0) { // Fast path: We already have the bytes in a contiguous buffer, so // just copy directly from it. bytes = buffer; pos = oldPos + size; tempPos = oldPos; } else if (size == 0) { return ""; } else if (size < 0) { throw InvalidProtocolBufferException.negativeSize(); } else if (size <= bufferSize) { refillBuffer(size); bytes = buffer; tempPos = 0; pos = tempPos + size; } else { // Slow path: Build a byte array first then copy it. bytes = readRawBytesSlowPath(size, /* ensureNoLeakedReferences= */ false); tempPos = 0; } return Utf8.decodeUtf8(bytes, tempPos, size); } @Override public void readGroup( final int fieldNumber, final MessageLite.Builder builder, final ExtensionRegistryLite extensionRegistry) throws IOException { checkRecursionLimit(); ++recursionDepth; builder.mergeFrom(this, extensionRegistry); checkLastTagWas(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); --recursionDepth; } @Override public T readGroup( final int fieldNumber, final Parser parser, final ExtensionRegistryLite extensionRegistry) throws IOException { checkRecursionLimit(); ++recursionDepth; T result = parser.parsePartialFrom(this, extensionRegistry); checkLastTagWas(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); --recursionDepth; return result; } @Deprecated @Override public void readUnknownGroup(final int fieldNumber, final MessageLite.Builder builder) throws IOException { readGroup(fieldNumber, builder, ExtensionRegistryLite.getEmptyRegistry()); } @Override public void readMessage( final MessageLite.Builder builder, final ExtensionRegistryLite extensionRegistry) throws IOException { final int length = readRawVarint32(); checkRecursionLimit(); final int oldLimit = pushLimit(length); ++recursionDepth; builder.mergeFrom(this, extensionRegistry); checkLastTagWas(0); --recursionDepth; if (getBytesUntilLimit() != 0) { throw InvalidProtocolBufferException.truncatedMessage(); } popLimit(oldLimit); } @Override public T readMessage( final Parser parser, final ExtensionRegistryLite extensionRegistry) throws IOException { int length = readRawVarint32(); checkRecursionLimit(); final int oldLimit = pushLimit(length); ++recursionDepth; T result = parser.parsePartialFrom(this, extensionRegistry); checkLastTagWas(0); --recursionDepth; if (getBytesUntilLimit() != 0) { throw InvalidProtocolBufferException.truncatedMessage(); } popLimit(oldLimit); return result; } @Override public ByteString readBytes() throws IOException { final int size = readRawVarint32(); if (size <= (bufferSize - pos) && size > 0) { // Fast path: We already have the bytes in a contiguous buffer, so // just copy directly from it. final ByteString result = ByteString.copyFrom(buffer, pos, size); pos += size; return result; } if (size == 0) { return ByteString.EMPTY; } if (size < 0) { throw InvalidProtocolBufferException.negativeSize(); } return readBytesSlowPath(size); } @Override public byte[] readByteArray() throws IOException { final int size = readRawVarint32(); if (size <= (bufferSize - pos) && size > 0) { // Fast path: We already have the bytes in a contiguous buffer, so // just copy directly from it. final byte[] result = Arrays.copyOfRange(buffer, pos, pos + size); pos += size; return result; } else if (size < 0) { throw InvalidProtocolBufferException.negativeSize(); } else { // Slow path: Build a byte array first then copy it. // TODO: Do we want to protect from malicious input streams here? return readRawBytesSlowPath(size, /* ensureNoLeakedReferences= */ false); } } @Override public ByteBuffer readByteBuffer() throws IOException { final int size = readRawVarint32(); if (size <= (bufferSize - pos) && size > 0) { // Fast path: We already have the bytes in a contiguous buffer. ByteBuffer result = ByteBuffer.wrap(Arrays.copyOfRange(buffer, pos, pos + size)); pos += size; return result; } if (size == 0) { return Internal.EMPTY_BYTE_BUFFER; } if (size < 0) { throw InvalidProtocolBufferException.negativeSize(); } // Slow path: Build a byte array first then copy it. // We must copy as the byte array was handed off to the InputStream and a malicious // implementation could retain a reference. return ByteBuffer.wrap(readRawBytesSlowPath(size, /* ensureNoLeakedReferences= */ true)); } @Override public int readUInt32() throws IOException { return readRawVarint32(); } @Override public int readEnum() throws IOException { return readRawVarint32(); } @Override public int readSFixed32() throws IOException { return readRawLittleEndian32(); } @Override public long readSFixed64() throws IOException { return readRawLittleEndian64(); } @Override public int readSInt32() throws IOException { return decodeZigZag32(readRawVarint32()); } @Override public long readSInt64() throws IOException { return decodeZigZag64(readRawVarint64()); } // ================================================================= @Override public int readRawVarint32() throws IOException { // See implementation notes for readRawVarint64 fastpath: { int tempPos = pos; if (bufferSize == tempPos) { break fastpath; } final byte[] buffer = this.buffer; int x; if ((x = buffer[tempPos++]) >= 0) { pos = tempPos; return x; } else if (bufferSize - tempPos < 9) { break fastpath; } else if ((x ^= (buffer[tempPos++] << 7)) < 0) { x ^= (~0 << 7); } else if ((x ^= (buffer[tempPos++] << 14)) >= 0) { x ^= (~0 << 7) ^ (~0 << 14); } else if ((x ^= (buffer[tempPos++] << 21)) < 0) { x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21); } else { int y = buffer[tempPos++]; x ^= y << 28; x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21) ^ (~0 << 28); if (y < 0 && buffer[tempPos++] < 0 && buffer[tempPos++] < 0 && buffer[tempPos++] < 0 && buffer[tempPos++] < 0 && buffer[tempPos++] < 0) { break fastpath; // Will throw malformedVarint() } } pos = tempPos; return x; } return (int) readRawVarint64SlowPath(); } private void skipRawVarint() throws IOException { if (bufferSize - pos >= MAX_VARINT_SIZE) { skipRawVarintFastPath(); } else { skipRawVarintSlowPath(); } } private void skipRawVarintFastPath() throws IOException { for (int i = 0; i < MAX_VARINT_SIZE; i++) { if (buffer[pos++] >= 0) { return; } } throw InvalidProtocolBufferException.malformedVarint(); } private void skipRawVarintSlowPath() throws IOException { for (int i = 0; i < MAX_VARINT_SIZE; i++) { if (readRawByte() >= 0) { return; } } throw InvalidProtocolBufferException.malformedVarint(); } @Override public long readRawVarint64() 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. fastpath: { int tempPos = pos; if (bufferSize == tempPos) { break fastpath; } final byte[] buffer = this.buffer; long x; int y; if ((y = buffer[tempPos++]) >= 0) { pos = tempPos; return y; } else if (bufferSize - tempPos < 9) { break fastpath; } else if ((y ^= (buffer[tempPos++] << 7)) < 0) { x = y ^ (~0 << 7); } else if ((y ^= (buffer[tempPos++] << 14)) >= 0) { x = y ^ ((~0 << 7) ^ (~0 << 14)); } else if ((y ^= (buffer[tempPos++] << 21)) < 0) { x = y ^ ((~0 << 7) ^ (~0 << 14) ^ (~0 << 21)); } else if ((x = y ^ ((long) buffer[tempPos++] << 28)) >= 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28); } else if ((x ^= ((long) buffer[tempPos++] << 35)) < 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35); } else if ((x ^= ((long) buffer[tempPos++] << 42)) >= 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42); } else if ((x ^= ((long) buffer[tempPos++] << 49)) < 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42) ^ (~0L << 49); } else { x ^= ((long) buffer[tempPos++] << 56); x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42) ^ (~0L << 49) ^ (~0L << 56); if (x < 0L) { if (buffer[tempPos++] < 0L) { break fastpath; // Will throw malformedVarint() } } } pos = tempPos; return x; } return readRawVarint64SlowPath(); } @Override long readRawVarint64SlowPath() throws IOException { long result = 0; for (int shift = 0; shift < 64; shift += 7) { final byte b = readRawByte(); result |= (long) (b & 0x7F) << shift; if ((b & 0x80) == 0) { return result; } } throw InvalidProtocolBufferException.malformedVarint(); } @Override public int readRawLittleEndian32() throws IOException { int tempPos = pos; if (bufferSize - tempPos < FIXED32_SIZE) { refillBuffer(FIXED32_SIZE); tempPos = pos; } final byte[] buffer = this.buffer; pos = tempPos + FIXED32_SIZE; return ((buffer[tempPos] & 0xff) | ((buffer[tempPos + 1] & 0xff) << 8) | ((buffer[tempPos + 2] & 0xff) << 16) | ((buffer[tempPos + 3] & 0xff) << 24)); } @Override public long readRawLittleEndian64() throws IOException { int tempPos = pos; if (bufferSize - tempPos < FIXED64_SIZE) { refillBuffer(FIXED64_SIZE); tempPos = pos; } final byte[] buffer = this.buffer; pos = tempPos + FIXED64_SIZE; return (((buffer[tempPos] & 0xffL)) | ((buffer[tempPos + 1] & 0xffL) << 8) | ((buffer[tempPos + 2] & 0xffL) << 16) | ((buffer[tempPos + 3] & 0xffL) << 24) | ((buffer[tempPos + 4] & 0xffL) << 32) | ((buffer[tempPos + 5] & 0xffL) << 40) | ((buffer[tempPos + 6] & 0xffL) << 48) | ((buffer[tempPos + 7] & 0xffL) << 56)); } // ----------------------------------------------------------------- @Override public void enableAliasing(boolean enabled) { // TODO: Ideally we should throw here. Do nothing for backward compatibility. } @Override public void resetSizeCounter() { totalBytesRetired = -pos; } @Override public int pushLimit(int byteLimit) throws InvalidProtocolBufferException { if (byteLimit < 0) { throw InvalidProtocolBufferException.negativeSize(); } byteLimit += totalBytesRetired + pos; if (byteLimit < 0) { throw InvalidProtocolBufferException.parseFailure(); } final int oldLimit = currentLimit; if (byteLimit > oldLimit) { throw InvalidProtocolBufferException.truncatedMessage(); } currentLimit = byteLimit; recomputeBufferSizeAfterLimit(); return oldLimit; } private void recomputeBufferSizeAfterLimit() { bufferSize += bufferSizeAfterLimit; final int bufferEnd = totalBytesRetired + bufferSize; if (bufferEnd > currentLimit) { // Limit is in current buffer. bufferSizeAfterLimit = bufferEnd - currentLimit; bufferSize -= bufferSizeAfterLimit; } else { bufferSizeAfterLimit = 0; } } @Override public void popLimit(final int oldLimit) { currentLimit = oldLimit; recomputeBufferSizeAfterLimit(); } @Override public int getBytesUntilLimit() { if (currentLimit == Integer.MAX_VALUE) { return -1; } final int currentAbsolutePosition = totalBytesRetired + pos; return currentLimit - currentAbsolutePosition; } @Override public boolean isAtEnd() throws IOException { return pos == bufferSize && !tryRefillBuffer(1); } @Override public int getTotalBytesRead() { return totalBytesRetired + pos; } private interface RefillCallback { void onRefill(); } private RefillCallback refillCallback = null; /** * Reads more bytes from the input, making at least {@code n} bytes available in the buffer. * Caller must ensure that the requested space is not yet available, and that the requested * space is less than BUFFER_SIZE. * * @throws InvalidProtocolBufferException The end of the stream or the current limit was * reached. */ private void refillBuffer(int n) throws IOException { if (!tryRefillBuffer(n)) { // We have to distinguish the exception between sizeLimitExceeded and truncatedMessage. So // we just throw an sizeLimitExceeded exception here if it exceeds the sizeLimit if (n > sizeLimit - totalBytesRetired - pos) { throw InvalidProtocolBufferException.sizeLimitExceeded(); } else { throw InvalidProtocolBufferException.truncatedMessage(); } } } /** * Tries to read more bytes from the input, making at least {@code n} bytes available in the * buffer. Caller must ensure that the requested space is not yet available, and that the * requested space is less than BUFFER_SIZE. * * @return {@code true} If the bytes could be made available; {@code false} 1. Current at the * end of the stream 2. The current limit was reached 3. The total size limit was reached */ private boolean tryRefillBuffer(int n) throws IOException { if (pos + n <= bufferSize) { throw new IllegalStateException( "refillBuffer() called when " + n + " bytes were already available in buffer"); } // Check whether the size of total message needs to read is bigger than the size limit. // We shouldn't throw an exception here as isAtEnd() function needs to get this function's // return as the result. if (n > sizeLimit - totalBytesRetired - pos) { return false; } // Shouldn't throw the exception here either. if (totalBytesRetired + pos + n > currentLimit) { // Oops, we hit a limit. return false; } if (refillCallback != null) { refillCallback.onRefill(); } int tempPos = pos; if (tempPos > 0) { if (bufferSize > tempPos) { System.arraycopy(buffer, tempPos, buffer, 0, bufferSize - tempPos); } totalBytesRetired += tempPos; bufferSize -= tempPos; pos = 0; } // Here we should refill the buffer as many bytes as possible. int bytesRead = read( input, buffer, bufferSize, Math.min( // the size of allocated but unused bytes in the buffer buffer.length - bufferSize, // do not exceed the total bytes limit sizeLimit - totalBytesRetired - bufferSize)); if (bytesRead == 0 || bytesRead < -1 || bytesRead > buffer.length) { throw new IllegalStateException( input.getClass() + "#read(byte[]) returned invalid result: " + bytesRead + "\nThe InputStream implementation is buggy."); } if (bytesRead > 0) { bufferSize += bytesRead; recomputeBufferSizeAfterLimit(); return (bufferSize >= n) ? true : tryRefillBuffer(n); } return false; } @Override public byte readRawByte() throws IOException { if (pos == bufferSize) { refillBuffer(1); } return buffer[pos++]; } @Override public byte[] readRawBytes(final int size) throws IOException { final int tempPos = pos; if (size <= (bufferSize - tempPos) && size > 0) { pos = tempPos + size; return Arrays.copyOfRange(buffer, tempPos, tempPos + size); } else { // TODO: Do we want to protect from malicious input streams here? return readRawBytesSlowPath(size, /* ensureNoLeakedReferences= */ false); } } /** * Exactly like readRawBytes, but caller must have already checked the fast path: (size <= * (bufferSize - pos) && size > 0) * *

If ensureNoLeakedReferences is true, the value is guaranteed to have not escaped to * untrusted code. */ private byte[] readRawBytesSlowPath(final int size, boolean ensureNoLeakedReferences) throws IOException { // Attempt to read the data in one byte array when it's safe to do. byte[] result = readRawBytesSlowPathOneChunk(size); if (result != null) { return ensureNoLeakedReferences ? result.clone() : result; } final int originalBufferPos = pos; final int bufferedBytes = bufferSize - pos; // Mark the current buffer consumed. totalBytesRetired += bufferSize; pos = 0; bufferSize = 0; // Determine the number of bytes we need to read from the input stream. int sizeLeft = size - bufferedBytes; // The size is very large. For security reasons we read them in small // chunks. List chunks = readRawBytesSlowPathRemainingChunks(sizeLeft); // OK, got everything. Now concatenate it all into one buffer. final byte[] bytes = new byte[size]; // Start by copying the leftover bytes from this.buffer. System.arraycopy(buffer, originalBufferPos, bytes, 0, bufferedBytes); // And now all the chunks. int tempPos = bufferedBytes; for (final byte[] chunk : chunks) { System.arraycopy(chunk, 0, bytes, tempPos, chunk.length); tempPos += chunk.length; } // Done. return bytes; } /** * Attempts to read the data in one byte array when it's safe to do. Returns null if the size to * read is too large and needs to be allocated in smaller chunks for security reasons. * *

Returns a byte[] that may have escaped to user code via InputStream APIs. */ private byte[] readRawBytesSlowPathOneChunk(final int size) throws IOException { if (size == 0) { return Internal.EMPTY_BYTE_ARRAY; } if (size < 0) { throw InvalidProtocolBufferException.negativeSize(); } // Integer-overflow-conscious check that the message size so far has not exceeded sizeLimit. int currentMessageSize = totalBytesRetired + pos + size; if (currentMessageSize - sizeLimit > 0) { throw InvalidProtocolBufferException.sizeLimitExceeded(); } // Verify that the message size so far has not exceeded currentLimit. if (currentMessageSize > currentLimit) { // Read to the end of the stream anyway. skipRawBytes(currentLimit - totalBytesRetired - pos); throw InvalidProtocolBufferException.truncatedMessage(); } final int bufferedBytes = bufferSize - pos; // Determine the number of bytes we need to read from the input stream. int sizeLeft = size - bufferedBytes; // TODO: Consider using a value larger than DEFAULT_BUFFER_SIZE. if (sizeLeft < DEFAULT_BUFFER_SIZE || sizeLeft <= available(input)) { // Either the bytes we need are known to be available, or the required buffer is // within an allowed threshold - go ahead and allocate the buffer now. final byte[] bytes = new byte[size]; // Copy all of the buffered bytes to the result buffer. System.arraycopy(buffer, pos, bytes, 0, bufferedBytes); totalBytesRetired += bufferSize; pos = 0; bufferSize = 0; // Fill the remaining bytes from the input stream. int tempPos = bufferedBytes; while (tempPos < bytes.length) { int n = read(input, bytes, tempPos, size - tempPos); if (n == -1) { throw InvalidProtocolBufferException.truncatedMessage(); } totalBytesRetired += n; tempPos += n; } return bytes; } return null; } /** * Reads the remaining data in small chunks from the input stream. * *

Returns a byte[] that may have escaped to user code via InputStream APIs. */ private List readRawBytesSlowPathRemainingChunks(int sizeLeft) throws IOException { // The size is very large. For security reasons, we can't allocate the // entire byte array yet. The size comes directly from the input, so a // maliciously-crafted message could provide a bogus very large size in // order to trick the app into allocating a lot of memory. We avoid this // by allocating and reading only a small chunk at a time, so that the // malicious message must actually *be* extremely large to cause // problems. Meanwhile, we limit the allowed size of a message elsewhere. final List chunks = new ArrayList<>(); while (sizeLeft > 0) { // TODO: Consider using a value larger than DEFAULT_BUFFER_SIZE. final byte[] chunk = new byte[Math.min(sizeLeft, DEFAULT_BUFFER_SIZE)]; int tempPos = 0; while (tempPos < chunk.length) { final int n = input.read(chunk, tempPos, chunk.length - tempPos); if (n == -1) { throw InvalidProtocolBufferException.truncatedMessage(); } totalBytesRetired += n; tempPos += n; } sizeLeft -= chunk.length; chunks.add(chunk); } return chunks; } /** * Like readBytes, but caller must have already checked the fast path: (size <= (bufferSize - * pos) && size > 0 || size == 0) */ private ByteString readBytesSlowPath(final int size) throws IOException { final byte[] result = readRawBytesSlowPathOneChunk(size); if (result != null) { // We must copy as the byte array was handed off to the InputStream and a malicious // implementation could retain a reference. return ByteString.copyFrom(result); } final int originalBufferPos = pos; final int bufferedBytes = bufferSize - pos; // Mark the current buffer consumed. totalBytesRetired += bufferSize; pos = 0; bufferSize = 0; // Determine the number of bytes we need to read from the input stream. int sizeLeft = size - bufferedBytes; // The size is very large. For security reasons we read them in small // chunks. List chunks = readRawBytesSlowPathRemainingChunks(sizeLeft); // OK, got everything. Now concatenate it all into one buffer. final byte[] bytes = new byte[size]; // Start by copying the leftover bytes from this.buffer. System.arraycopy(buffer, originalBufferPos, bytes, 0, bufferedBytes); // And now all the chunks. int tempPos = bufferedBytes; for (final byte[] chunk : chunks) { System.arraycopy(chunk, 0, bytes, tempPos, chunk.length); tempPos += chunk.length; } return ByteString.wrap(bytes); } @Override public void skipRawBytes(final int size) throws IOException { if (size <= (bufferSize - pos) && size >= 0) { // We have all the bytes we need already. pos += size; } else { skipRawBytesSlowPath(size); } } /** * Exactly like skipRawBytes, but caller must have already checked the fast path: (size <= * (bufferSize - pos) && size >= 0) */ private void skipRawBytesSlowPath(final int size) throws IOException { if (size < 0) { throw InvalidProtocolBufferException.negativeSize(); } if (totalBytesRetired + pos + size > currentLimit) { // Read to the end of the stream anyway. skipRawBytes(currentLimit - totalBytesRetired - pos); // Then fail. throw InvalidProtocolBufferException.truncatedMessage(); } int totalSkipped = 0; if (refillCallback == null) { // Skipping more bytes than are in the buffer. First skip what we have. totalBytesRetired += pos; totalSkipped = bufferSize - pos; bufferSize = 0; pos = 0; try { while (totalSkipped < size) { int toSkip = size - totalSkipped; long skipped = skip(input, toSkip); if (skipped < 0 || skipped > toSkip) { throw new IllegalStateException( input.getClass() + "#skip returned invalid result: " + skipped + "\nThe InputStream implementation is buggy."); } else if (skipped == 0) { // The API contract of skip() permits an inputstream to skip zero bytes for any reason // it wants. In particular, ByteArrayInputStream will just return zero over and over // when it's at the end of its input. In order to actually confirm that we've hit the // end of input, we need to issue a read call via the other path. break; } totalSkipped += (int) skipped; } } finally { totalBytesRetired += totalSkipped; recomputeBufferSizeAfterLimit(); } } if (totalSkipped < size) { // Skipping more bytes than are in the buffer. First skip what we have. int tempPos = bufferSize - pos; pos = bufferSize; // Keep refilling the buffer until we get to the point we wanted to skip to. // This has the side effect of ensuring the limits are updated correctly. refillBuffer(1); while (size - tempPos > bufferSize) { tempPos += bufferSize; pos = bufferSize; refillBuffer(1); } pos = size - tempPos; } } } /** * Implementation of {@link CodedInputStream} that uses an {@link Iterable } as the * data source. Requires the use of {@code sun.misc.Unsafe} to perform fast reads on the buffer. */ private static final class IterableDirectByteBufferDecoder extends CodedInputStream { /** The object that need to decode. */ private final Iterable input; /** The {@link Iterator} with type {@link ByteBuffer} of {@code input} */ private final Iterator iterator; /** The current ByteBuffer; */ private ByteBuffer currentByteBuffer; /** * If {@code true}, indicates that all the buffers are backing a {@link ByteString} and are * therefore considered to be an immutable input source. */ private final boolean immutable; /** * If {@code true}, indicates that calls to read {@link ByteString} or {@code byte[]} * may return slices of the underlying buffer, rather than copies. */ private boolean enableAliasing; /** The global total message length limit */ private int totalBufferSize; /** The amount of available data in the input beyond {@link #currentLimit}. */ private int bufferSizeAfterCurrentLimit; /** The absolute position of the end of the current message. */ private int currentLimit = Integer.MAX_VALUE; /** The last tag that was read from this stream. */ private int lastTag; /** Total Bytes have been Read from the {@link Iterable} {@link ByteBuffer} */ private int totalBytesRead; /** The start position offset of the whole message, used as to reset the totalBytesRead */ private int startOffset; /** The current position for current ByteBuffer */ private long currentByteBufferPos; private long currentByteBufferStartPos; /** * If the current ByteBuffer is unsafe-direct based, currentAddress is the start address of this * ByteBuffer; otherwise should be zero. */ private long currentAddress; /** The limit position for current ByteBuffer */ private long currentByteBufferLimit; /** * The constructor of {@code Iterable} decoder. * * @param inputBufs The input data. * @param size The total size of the input data. * @param immutableFlag whether the input data is immutable. */ private IterableDirectByteBufferDecoder( Iterable inputBufs, int size, boolean immutableFlag) { totalBufferSize = size; input = inputBufs; iterator = input.iterator(); immutable = immutableFlag; startOffset = totalBytesRead = 0; if (size == 0) { currentByteBuffer = EMPTY_BYTE_BUFFER; currentByteBufferPos = 0; currentByteBufferStartPos = 0; currentByteBufferLimit = 0; currentAddress = 0; } else { tryGetNextByteBuffer(); } } /** To get the next ByteBuffer from {@code input}, and then update the parameters */ private void getNextByteBuffer() throws InvalidProtocolBufferException { if (!iterator.hasNext()) { throw InvalidProtocolBufferException.truncatedMessage(); } tryGetNextByteBuffer(); } private void tryGetNextByteBuffer() { currentByteBuffer = iterator.next(); totalBytesRead += (int) (currentByteBufferPos - currentByteBufferStartPos); currentByteBufferPos = currentByteBuffer.position(); currentByteBufferStartPos = currentByteBufferPos; currentByteBufferLimit = currentByteBuffer.limit(); currentAddress = UnsafeUtil.addressOffset(currentByteBuffer); currentByteBufferPos += currentAddress; currentByteBufferStartPos += currentAddress; currentByteBufferLimit += currentAddress; } @Override public int readTag() throws IOException { if (isAtEnd()) { lastTag = 0; return 0; } lastTag = readRawVarint32(); if (WireFormat.getTagFieldNumber(lastTag) == 0) { // If we actually read zero (or any tag number corresponding to field // number zero), that's not a valid tag. throw InvalidProtocolBufferException.invalidTag(); } return lastTag; } @Override public void checkLastTagWas(final int value) throws InvalidProtocolBufferException { if (lastTag != value) { throw InvalidProtocolBufferException.invalidEndTag(); } } @Override public int getLastTag() { return lastTag; } @Override public boolean skipField(final int tag) throws IOException { switch (WireFormat.getTagWireType(tag)) { case WireFormat.WIRETYPE_VARINT: skipRawVarint(); return true; case WireFormat.WIRETYPE_FIXED64: skipRawBytes(FIXED64_SIZE); return true; case WireFormat.WIRETYPE_LENGTH_DELIMITED: skipRawBytes(readRawVarint32()); return true; case WireFormat.WIRETYPE_START_GROUP: skipMessage(); checkLastTagWas( WireFormat.makeTag(WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP)); return true; case WireFormat.WIRETYPE_END_GROUP: return false; case WireFormat.WIRETYPE_FIXED32: skipRawBytes(FIXED32_SIZE); return true; default: throw InvalidProtocolBufferException.invalidWireType(); } } @Override public boolean skipField(final int tag, final CodedOutputStream output) throws IOException { switch (WireFormat.getTagWireType(tag)) { case WireFormat.WIRETYPE_VARINT: { long value = readInt64(); output.writeUInt32NoTag(tag); output.writeUInt64NoTag(value); return true; } case WireFormat.WIRETYPE_FIXED64: { long value = readRawLittleEndian64(); output.writeUInt32NoTag(tag); output.writeFixed64NoTag(value); return true; } case WireFormat.WIRETYPE_LENGTH_DELIMITED: { ByteString value = readBytes(); output.writeUInt32NoTag(tag); output.writeBytesNoTag(value); return true; } case WireFormat.WIRETYPE_START_GROUP: { output.writeUInt32NoTag(tag); skipMessage(output); int endtag = WireFormat.makeTag( WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP); checkLastTagWas(endtag); output.writeUInt32NoTag(endtag); return true; } case WireFormat.WIRETYPE_END_GROUP: { return false; } case WireFormat.WIRETYPE_FIXED32: { int value = readRawLittleEndian32(); output.writeUInt32NoTag(tag); output.writeFixed32NoTag(value); return true; } default: throw InvalidProtocolBufferException.invalidWireType(); } } // ----------------------------------------------------------------- @Override public double readDouble() throws IOException { return Double.longBitsToDouble(readRawLittleEndian64()); } @Override public float readFloat() throws IOException { return Float.intBitsToFloat(readRawLittleEndian32()); } @Override public long readUInt64() throws IOException { return readRawVarint64(); } @Override public long readInt64() throws IOException { return readRawVarint64(); } @Override public int readInt32() throws IOException { return readRawVarint32(); } @Override public long readFixed64() throws IOException { return readRawLittleEndian64(); } @Override public int readFixed32() throws IOException { return readRawLittleEndian32(); } @Override public boolean readBool() throws IOException { return readRawVarint64() != 0; } @Override public String readString() throws IOException { final int size = readRawVarint32(); if (size > 0 && size <= currentByteBufferLimit - currentByteBufferPos) { byte[] bytes = new byte[size]; UnsafeUtil.copyMemory(currentByteBufferPos, bytes, 0, size); String result = new String(bytes, UTF_8); currentByteBufferPos += size; return result; } else if (size > 0 && size <= remaining()) { // TODO: To use an underlying bytes[] instead of allocating a new bytes[] byte[] bytes = new byte[size]; readRawBytesTo(bytes, 0, size); String result = new String(bytes, UTF_8); return result; } if (size == 0) { return ""; } if (size < 0) { throw InvalidProtocolBufferException.negativeSize(); } throw InvalidProtocolBufferException.truncatedMessage(); } @Override public String readStringRequireUtf8() throws IOException { final int size = readRawVarint32(); if (size > 0 && size <= currentByteBufferLimit - currentByteBufferPos) { final int bufferPos = (int) (currentByteBufferPos - currentByteBufferStartPos); String result = Utf8.decodeUtf8(currentByteBuffer, bufferPos, size); currentByteBufferPos += size; return result; } if (size >= 0 && size <= remaining()) { byte[] bytes = new byte[size]; readRawBytesTo(bytes, 0, size); return Utf8.decodeUtf8(bytes, 0, size); } if (size == 0) { return ""; } if (size <= 0) { throw InvalidProtocolBufferException.negativeSize(); } throw InvalidProtocolBufferException.truncatedMessage(); } @Override public void readGroup( final int fieldNumber, final MessageLite.Builder builder, final ExtensionRegistryLite extensionRegistry) throws IOException { checkRecursionLimit(); ++recursionDepth; builder.mergeFrom(this, extensionRegistry); checkLastTagWas(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); --recursionDepth; } @Override public T readGroup( final int fieldNumber, final Parser parser, final ExtensionRegistryLite extensionRegistry) throws IOException { checkRecursionLimit(); ++recursionDepth; T result = parser.parsePartialFrom(this, extensionRegistry); checkLastTagWas(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); --recursionDepth; return result; } @Deprecated @Override public void readUnknownGroup(final int fieldNumber, final MessageLite.Builder builder) throws IOException { readGroup(fieldNumber, builder, ExtensionRegistryLite.getEmptyRegistry()); } @Override public void readMessage( final MessageLite.Builder builder, final ExtensionRegistryLite extensionRegistry) throws IOException { final int length = readRawVarint32(); checkRecursionLimit(); final int oldLimit = pushLimit(length); ++recursionDepth; builder.mergeFrom(this, extensionRegistry); checkLastTagWas(0); --recursionDepth; if (getBytesUntilLimit() != 0) { throw InvalidProtocolBufferException.truncatedMessage(); } popLimit(oldLimit); } @Override public T readMessage( final Parser parser, final ExtensionRegistryLite extensionRegistry) throws IOException { int length = readRawVarint32(); checkRecursionLimit(); final int oldLimit = pushLimit(length); ++recursionDepth; T result = parser.parsePartialFrom(this, extensionRegistry); checkLastTagWas(0); --recursionDepth; if (getBytesUntilLimit() != 0) { throw InvalidProtocolBufferException.truncatedMessage(); } popLimit(oldLimit); return result; } @Override public ByteString readBytes() throws IOException { final int size = readRawVarint32(); if (size > 0 && size <= currentByteBufferLimit - currentByteBufferPos) { if (immutable && enableAliasing) { final int idx = (int) (currentByteBufferPos - currentAddress); final ByteString result = ByteString.wrap(slice(idx, idx + size)); currentByteBufferPos += size; return result; } else { byte[] bytes = new byte[size]; UnsafeUtil.copyMemory(currentByteBufferPos, bytes, 0, size); currentByteBufferPos += size; return ByteString.wrap(bytes); } } else if (size > 0 && size <= remaining()) { if (immutable && enableAliasing) { ArrayList byteStrings = new ArrayList<>(); int l = size; while (l > 0) { if (currentRemaining() == 0) { getNextByteBuffer(); } int bytesToCopy = Math.min(l, (int) currentRemaining()); int idx = (int) (currentByteBufferPos - currentAddress); byteStrings.add(ByteString.wrap(slice(idx, idx + bytesToCopy))); l -= bytesToCopy; currentByteBufferPos += bytesToCopy; } return ByteString.copyFrom(byteStrings); } else { byte[] temp = new byte[size]; readRawBytesTo(temp, 0, size); return ByteString.wrap(temp); } } if (size == 0) { return ByteString.EMPTY; } if (size < 0) { throw InvalidProtocolBufferException.negativeSize(); } throw InvalidProtocolBufferException.truncatedMessage(); } @Override public byte[] readByteArray() throws IOException { return readRawBytes(readRawVarint32()); } @Override public ByteBuffer readByteBuffer() throws IOException { final int size = readRawVarint32(); if (size > 0 && size <= currentRemaining()) { if (!immutable && enableAliasing) { currentByteBufferPos += size; return slice( (int) (currentByteBufferPos - currentAddress - size), (int) (currentByteBufferPos - currentAddress)); } else { byte[] bytes = new byte[size]; UnsafeUtil.copyMemory(currentByteBufferPos, bytes, 0, size); currentByteBufferPos += size; return ByteBuffer.wrap(bytes); } } else if (size > 0 && size <= remaining()) { byte[] temp = new byte[size]; readRawBytesTo(temp, 0, size); return ByteBuffer.wrap(temp); } if (size == 0) { return EMPTY_BYTE_BUFFER; } if (size < 0) { throw InvalidProtocolBufferException.negativeSize(); } throw InvalidProtocolBufferException.truncatedMessage(); } @Override public int readUInt32() throws IOException { return readRawVarint32(); } @Override public int readEnum() throws IOException { return readRawVarint32(); } @Override public int readSFixed32() throws IOException { return readRawLittleEndian32(); } @Override public long readSFixed64() throws IOException { return readRawLittleEndian64(); } @Override public int readSInt32() throws IOException { return decodeZigZag32(readRawVarint32()); } @Override public long readSInt64() throws IOException { return decodeZigZag64(readRawVarint64()); } @Override public int readRawVarint32() throws IOException { fastpath: { long tempPos = currentByteBufferPos; if (currentByteBufferLimit == currentByteBufferPos) { break fastpath; } int x; if ((x = UnsafeUtil.getByte(tempPos++)) >= 0) { currentByteBufferPos++; return x; } else if (currentByteBufferLimit - currentByteBufferPos < 10) { break fastpath; } else if ((x ^= (UnsafeUtil.getByte(tempPos++) << 7)) < 0) { x ^= (~0 << 7); } else if ((x ^= (UnsafeUtil.getByte(tempPos++) << 14)) >= 0) { x ^= (~0 << 7) ^ (~0 << 14); } else if ((x ^= (UnsafeUtil.getByte(tempPos++) << 21)) < 0) { x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21); } else { int y = UnsafeUtil.getByte(tempPos++); x ^= y << 28; x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21) ^ (~0 << 28); if (y < 0 && UnsafeUtil.getByte(tempPos++) < 0 && UnsafeUtil.getByte(tempPos++) < 0 && UnsafeUtil.getByte(tempPos++) < 0 && UnsafeUtil.getByte(tempPos++) < 0 && UnsafeUtil.getByte(tempPos++) < 0) { break fastpath; // Will throw malformedVarint() } } currentByteBufferPos = tempPos; return x; } return (int) readRawVarint64SlowPath(); } @Override public long readRawVarint64() throws IOException { fastpath: { long tempPos = currentByteBufferPos; if (currentByteBufferLimit == currentByteBufferPos) { break fastpath; } long x; int y; if ((y = UnsafeUtil.getByte(tempPos++)) >= 0) { currentByteBufferPos++; return y; } else if (currentByteBufferLimit - currentByteBufferPos < 10) { break fastpath; } else if ((y ^= (UnsafeUtil.getByte(tempPos++) << 7)) < 0) { x = y ^ (~0 << 7); } else if ((y ^= (UnsafeUtil.getByte(tempPos++) << 14)) >= 0) { x = y ^ ((~0 << 7) ^ (~0 << 14)); } else if ((y ^= (UnsafeUtil.getByte(tempPos++) << 21)) < 0) { x = y ^ ((~0 << 7) ^ (~0 << 14) ^ (~0 << 21)); } else if ((x = y ^ ((long) UnsafeUtil.getByte(tempPos++) << 28)) >= 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28); } else if ((x ^= ((long) UnsafeUtil.getByte(tempPos++) << 35)) < 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35); } else if ((x ^= ((long) UnsafeUtil.getByte(tempPos++) << 42)) >= 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42); } else if ((x ^= ((long) UnsafeUtil.getByte(tempPos++) << 49)) < 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42) ^ (~0L << 49); } else { x ^= ((long) UnsafeUtil.getByte(tempPos++) << 56); x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42) ^ (~0L << 49) ^ (~0L << 56); if (x < 0L) { if (UnsafeUtil.getByte(tempPos++) < 0L) { break fastpath; // Will throw malformedVarint() } } } currentByteBufferPos = tempPos; return x; } return readRawVarint64SlowPath(); } @Override long readRawVarint64SlowPath() throws IOException { long result = 0; for (int shift = 0; shift < 64; shift += 7) { final byte b = readRawByte(); result |= (long) (b & 0x7F) << shift; if ((b & 0x80) == 0) { return result; } } throw InvalidProtocolBufferException.malformedVarint(); } @Override public int readRawLittleEndian32() throws IOException { if (currentRemaining() >= FIXED32_SIZE) { long tempPos = currentByteBufferPos; currentByteBufferPos += FIXED32_SIZE; return ((UnsafeUtil.getByte(tempPos) & 0xff) | ((UnsafeUtil.getByte(tempPos + 1) & 0xff) << 8) | ((UnsafeUtil.getByte(tempPos + 2) & 0xff) << 16) | ((UnsafeUtil.getByte(tempPos + 3) & 0xff) << 24)); } return ((readRawByte() & 0xff) | ((readRawByte() & 0xff) << 8) | ((readRawByte() & 0xff) << 16) | ((readRawByte() & 0xff) << 24)); } @Override public long readRawLittleEndian64() throws IOException { if (currentRemaining() >= FIXED64_SIZE) { long tempPos = currentByteBufferPos; currentByteBufferPos += FIXED64_SIZE; return ((UnsafeUtil.getByte(tempPos) & 0xffL) | ((UnsafeUtil.getByte(tempPos + 1) & 0xffL) << 8) | ((UnsafeUtil.getByte(tempPos + 2) & 0xffL) << 16) | ((UnsafeUtil.getByte(tempPos + 3) & 0xffL) << 24) | ((UnsafeUtil.getByte(tempPos + 4) & 0xffL) << 32) | ((UnsafeUtil.getByte(tempPos + 5) & 0xffL) << 40) | ((UnsafeUtil.getByte(tempPos + 6) & 0xffL) << 48) | ((UnsafeUtil.getByte(tempPos + 7) & 0xffL) << 56)); } return ((readRawByte() & 0xffL) | ((readRawByte() & 0xffL) << 8) | ((readRawByte() & 0xffL) << 16) | ((readRawByte() & 0xffL) << 24) | ((readRawByte() & 0xffL) << 32) | ((readRawByte() & 0xffL) << 40) | ((readRawByte() & 0xffL) << 48) | ((readRawByte() & 0xffL) << 56)); } @Override public void enableAliasing(boolean enabled) { this.enableAliasing = enabled; } @Override public void resetSizeCounter() { startOffset = (int) (totalBytesRead + currentByteBufferPos - currentByteBufferStartPos); } @Override public int pushLimit(int byteLimit) throws InvalidProtocolBufferException { if (byteLimit < 0) { throw InvalidProtocolBufferException.negativeSize(); } byteLimit += getTotalBytesRead(); final int oldLimit = currentLimit; if (byteLimit > oldLimit) { throw InvalidProtocolBufferException.truncatedMessage(); } currentLimit = byteLimit; recomputeBufferSizeAfterLimit(); return oldLimit; } private void recomputeBufferSizeAfterLimit() { totalBufferSize += bufferSizeAfterCurrentLimit; final int bufferEnd = totalBufferSize - startOffset; if (bufferEnd > currentLimit) { // Limit is in current buffer. bufferSizeAfterCurrentLimit = bufferEnd - currentLimit; totalBufferSize -= bufferSizeAfterCurrentLimit; } else { bufferSizeAfterCurrentLimit = 0; } } @Override public void popLimit(final int oldLimit) { currentLimit = oldLimit; recomputeBufferSizeAfterLimit(); } @Override public int getBytesUntilLimit() { if (currentLimit == Integer.MAX_VALUE) { return -1; } return currentLimit - getTotalBytesRead(); } @Override public boolean isAtEnd() throws IOException { return totalBytesRead + currentByteBufferPos - currentByteBufferStartPos == totalBufferSize; } @Override public int getTotalBytesRead() { return (int) (totalBytesRead - startOffset + currentByteBufferPos - currentByteBufferStartPos); } @Override public byte readRawByte() throws IOException { if (currentRemaining() == 0) { getNextByteBuffer(); } return UnsafeUtil.getByte(currentByteBufferPos++); } @Override public byte[] readRawBytes(final int length) throws IOException { if (length >= 0 && length <= currentRemaining()) { byte[] bytes = new byte[length]; UnsafeUtil.copyMemory(currentByteBufferPos, bytes, 0, length); currentByteBufferPos += length; return bytes; } if (length >= 0 && length <= remaining()) { byte[] bytes = new byte[length]; readRawBytesTo(bytes, 0, length); return bytes; } if (length <= 0) { if (length == 0) { return EMPTY_BYTE_ARRAY; } else { throw InvalidProtocolBufferException.negativeSize(); } } throw InvalidProtocolBufferException.truncatedMessage(); } /** * Try to get raw bytes from {@code input} with the size of {@code length} and copy to {@code * bytes} array. If the size is bigger than the number of remaining bytes in the input, then * throw {@code truncatedMessage} exception. */ private void readRawBytesTo(byte[] bytes, int offset, final int length) throws IOException { if (length >= 0 && length <= remaining()) { int l = length; while (l > 0) { if (currentRemaining() == 0) { getNextByteBuffer(); } int bytesToCopy = Math.min(l, (int) currentRemaining()); UnsafeUtil.copyMemory(currentByteBufferPos, bytes, length - l + offset, bytesToCopy); l -= bytesToCopy; currentByteBufferPos += bytesToCopy; } return; } if (length <= 0) { if (length == 0) { return; } else { throw InvalidProtocolBufferException.negativeSize(); } } throw InvalidProtocolBufferException.truncatedMessage(); } @Override public void skipRawBytes(final int length) throws IOException { if (length >= 0 && length <= (totalBufferSize - totalBytesRead - currentByteBufferPos + currentByteBufferStartPos)) { // We have all the bytes we need already. int l = length; while (l > 0) { if (currentRemaining() == 0) { getNextByteBuffer(); } int rl = Math.min(l, (int) currentRemaining()); l -= rl; currentByteBufferPos += rl; } return; } if (length < 0) { throw InvalidProtocolBufferException.negativeSize(); } throw InvalidProtocolBufferException.truncatedMessage(); } // TODO: optimize to fastpath private void skipRawVarint() throws IOException { for (int i = 0; i < MAX_VARINT_SIZE; i++) { if (readRawByte() >= 0) { return; } } throw InvalidProtocolBufferException.malformedVarint(); } /** * Try to get the number of remaining bytes in {@code input}. * * @return the number of remaining bytes in {@code input}. */ private int remaining() { return (int) (totalBufferSize - totalBytesRead - currentByteBufferPos + currentByteBufferStartPos); } /** * Try to get the number of remaining bytes in {@code currentByteBuffer}. * * @return the number of remaining bytes in {@code currentByteBuffer} */ private long currentRemaining() { return (currentByteBufferLimit - currentByteBufferPos); } private ByteBuffer slice(int begin, int end) throws IOException { int prevPos = currentByteBuffer.position(); int prevLimit = currentByteBuffer.limit(); // View ByteBuffer as Buffer to avoid cross-Java version issues. // See https://issues.apache.org/jira/browse/MRESOLVER-85 Buffer asBuffer = currentByteBuffer; try { asBuffer.position(begin); asBuffer.limit(end); return currentByteBuffer.slice(); } catch (IllegalArgumentException e) { throw InvalidProtocolBufferException.truncatedMessage(); } finally { asBuffer.position(prevPos); asBuffer.limit(prevLimit); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy