| // Protocol Buffers - Google's data interchange format |
| // Copyright 2008 Google Inc. All rights reserved. |
| // https://developers.google.com/protocol-buffers/ |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| package com.google.protobuf; |
| |
| import static com.google.protobuf.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. |
| * |
| * <p>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 kenton@google.com 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 <ByteBuffer>}. */ |
| public static CodedInputStream newInstance(final Iterable<ByteBuffer> input) { |
| if (!UnsafeDirectNioDecoder.isSupported()) { |
| return newInstance(new IterableByteBufferInputStream(input)); |
| } |
| return newInstance(input, false); |
| } |
| |
| /** Create a new CodedInputStream wrapping the given {@code Iterable <ByteBuffer>}. */ |
| static CodedInputStream newInstance( |
| final Iterable<ByteBuffer> 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(yilunchong): 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(nathanmittler): 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 abstract void skipMessage() throws IOException; |
| |
| /** |
| * 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 abstract void skipMessage(CodedOutputStream output) throws IOException; |
| |
| |
| // ----------------------------------------------------------------- |
| |
| /** 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 extends MessageLite> T readGroup( |
| final int fieldNumber, final Parser<T> 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 extends MessageLite> T readMessage( |
| final Parser<T> 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. |
| * |
| * <p>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. |
| * |
| * <p>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. |
| * |
| * <p>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. |
| * |
| * <p>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 void skipMessage() throws IOException { |
| while (true) { |
| final int tag = readTag(); |
| if (tag == 0 || !skipField(tag)) { |
| return; |
| } |
| } |
| } |
| |
| @Override |
| public void skipMessage(CodedOutputStream output) throws IOException { |
| while (true) { |
| final int tag = readTag(); |
| if (tag == 0 || !skipField(tag, output)) { |
| return; |
| } |
| } |
| } |
| |
| |
| // ----------------------------------------------------------------- |
| |
| @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 extends MessageLite> T readGroup( |
| final int fieldNumber, |
| final Parser<T> 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 extends MessageLite> T readMessage( |
| final Parser<T> 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(nathanmittler): 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[]} |
| * <strong>may</strong> 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 void skipMessage() throws IOException { |
| while (true) { |
| final int tag = readTag(); |
| if (tag == 0 || !skipField(tag)) { |
| return; |
| } |
| } |
| } |
| |
| @Override |
| public void skipMessage(CodedOutputStream output) throws IOException { |
| while (true) { |
| final int tag = readTag(); |
| if (tag == 0 || !skipField(tag, output)) { |
| return; |
| } |
| } |
| } |
| |
| |
| // ----------------------------------------------------------------- |
| |
| @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(nathanmittler): Is there a way to avoid this copy? |
| // TODO(anuraaga): 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 extends MessageLite> T readGroup( |
| final int fieldNumber, |
| final Parser<T> 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 extends MessageLite> T readMessage( |
| final Parser<T> 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(nathanmittler): 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(); |
| } |
| } |
| |
| @Override |
| public void skipMessage() throws IOException { |
| while (true) { |
| final int tag = readTag(); |
| if (tag == 0 || !skipField(tag)) { |
| return; |
| } |
| } |
| } |
| |
| @Override |
| public void skipMessage(CodedOutputStream output) throws IOException { |
| while (true) { |
| final int tag = readTag(); |
| if (tag == 0 || !skipField(tag, output)) { |
| return; |
| } |
| } |
| } |
| |
| /** 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 <= 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 <= 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 extends MessageLite> T readGroup( |
| final int fieldNumber, |
| final Parser<T> 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 extends MessageLite> T readMessage( |
| final Parser<T> 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; |
| } |
| 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 { |
| // Slow path: Build a byte array first then copy it. |
| // TODO(dweis): 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; |
| } |
| // 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(nathanmittler): 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; |
| 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(dweis): 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<byte[]> 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(nathanmittler): 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<byte[]> 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<byte[]> chunks = new ArrayList<>(); |
| |
| while (sizeLeft > 0) { |
| // TODO(nathanmittler): 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<byte[]> 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 <ByteBuffer>} 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<ByteBuffer> input; |
| /** The {@link Iterator} with type {@link ByteBuffer} of {@code input} */ |
| private final Iterator<ByteBuffer> 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[]} |
| * <strong>may</strong> 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<ByteBuffer>} 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<ByteBuffer> 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 void skipMessage() throws IOException { |
| while (true) { |
| final int tag = readTag(); |
| if (tag == 0 || !skipField(tag)) { |
| return; |
| } |
| } |
| } |
| |
| @Override |
| public void skipMessage(CodedOutputStream output) throws IOException { |
| while (true) { |
| final int tag = readTag(); |
| if (tag == 0 || !skipField(tag, output)) { |
| return; |
| } |
| } |
| } |
| |
| // ----------------------------------------------------------------- |
| |
| @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(yilunchong): 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 extends MessageLite> T readGroup( |
| final int fieldNumber, |
| final Parser<T> 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 extends MessageLite> T readMessage( |
| final Parser<T> 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<ByteString> 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); |
| } |
| } |
| } |
| } |