hbase-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From st...@apache.org
Subject [23/51] [partial] hbase git commit: HBASE-15638 Shade protobuf Which includes
Date Tue, 04 Oct 2016 05:16:36 GMT
http://git-wip-us.apache.org/repos/asf/hbase/blob/95c1dc93/hbase-protocol-shaded/src/main/java/org/apache/hadoop/hbase/shaded/com/google/protobuf/CodedInputStream.java
----------------------------------------------------------------------
diff --git a/hbase-protocol-shaded/src/main/java/org/apache/hadoop/hbase/shaded/com/google/protobuf/CodedInputStream.java b/hbase-protocol-shaded/src/main/java/org/apache/hadoop/hbase/shaded/com/google/protobuf/CodedInputStream.java
new file mode 100644
index 0000000..12d70ce
--- /dev/null
+++ b/hbase-protocol-shaded/src/main/java/org/apache/hadoop/hbase/shaded/com/google/protobuf/CodedInputStream.java
@@ -0,0 +1,2895 @@
+// 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 org.apache.hadoop.hbase.shaded.com.google.protobuf;
+
+import static org.apache.hadoop.hbase.shaded.com.google.protobuf.Internal.EMPTY_BYTE_ARRAY;
+import static org.apache.hadoop.hbase.shaded.com.google.protobuf.Internal.EMPTY_BYTE_BUFFER;
+import static org.apache.hadoop.hbase.shaded.com.google.protobuf.Internal.UTF_8;
+import static org.apache.hadoop.hbase.shaded.com.google.protobuf.Internal.checkNotNull;
+import static org.apache.hadoop.hbase.shaded.com.google.protobuf.WireFormat.FIXED_32_SIZE;
+import static org.apache.hadoop.hbase.shaded.com.google.protobuf.WireFormat.FIXED_64_SIZE;
+import static org.apache.hadoop.hbase.shaded.com.google.protobuf.WireFormat.MAX_VARINT_SIZE;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+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;
+  private static final int DEFAULT_RECURSION_LIMIT = 100;
+  private static final int DEFAULT_SIZE_LIMIT = 64 << 20; // 64MB
+
+  /** Visible for subclasses. See setRecursionLimit() */
+  int recursionDepth;
+
+  int recursionLimit = DEFAULT_RECURSION_LIMIT;
+
+  /** Visible for subclasses. See setSizeLimit() */
+  int sizeLimit = DEFAULT_SIZE_LIMIT;
+
+  /** 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. */
+  static CodedInputStream newInstance(final InputStream input, int bufferSize) {
+    if (input == null) {
+      // TODO(nathanmittler): Ideally we should 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 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, false /* bufferIsImmutable */);
+  }
+
+  /** 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, false /* bufferIsImmutable */);
+  }
+
+  /** 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);
+  }
+
+  /** 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 64.
+   *
+   * @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 64MB. 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 (nor with {@link ByteString#newCodedInput}).
+   *
+   * <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;
+  }
+
+  /**
+   * 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)}.
+   */
+  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(FIXED_64_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(FIXED_32_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.writeRawVarint32(tag);
+            output.writeUInt64NoTag(value);
+            return true;
+          }
+        case WireFormat.WIRETYPE_FIXED64:
+          {
+            long value = readRawLittleEndian64();
+            output.writeRawVarint32(tag);
+            output.writeFixed64NoTag(value);
+            return true;
+          }
+        case WireFormat.WIRETYPE_LENGTH_DELIMITED:
+          {
+            ByteString value = readBytes();
+            output.writeRawVarint32(tag);
+            output.writeBytesNoTag(value);
+            return true;
+          }
+        case WireFormat.WIRETYPE_START_GROUP:
+          {
+            output.writeRawVarint32(tag);
+            skipMessage(output);
+            int endtag =
+                WireFormat.makeTag(
+                    WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP);
+            checkLastTagWas(endtag);
+            output.writeRawVarint32(endtag);
+            return true;
+          }
+        case WireFormat.WIRETYPE_END_GROUP:
+          {
+            return false;
+          }
+        case WireFormat.WIRETYPE_FIXED32:
+          {
+            int value = readRawLittleEndian32();
+            output.writeRawVarint32(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)) {
+        // TODO(martinrb): We could save a pass by validating while decoding.
+        if (!Utf8.isValidUtf8(buffer, pos, pos + size)) {
+          throw InvalidProtocolBufferException.invalidUtf8();
+        }
+        final int tempPos = pos;
+        pos += size;
+        return new String(buffer, tempPos, size, UTF_8);
+      }
+
+      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 {
+      if (recursionDepth >= recursionLimit) {
+        throw InvalidProtocolBufferException.recursionLimitExceeded();
+      }
+      ++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 {
+      if (recursionDepth >= recursionLimit) {
+        throw InvalidProtocolBufferException.recursionLimitExceeded();
+      }
+      ++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();
+      if (recursionDepth >= recursionLimit) {
+        throw InvalidProtocolBufferException.recursionLimitExceeded();
+      }
+      final int oldLimit = pushLimit(length);
+      ++recursionDepth;
+      builder.mergeFrom(this, extensionRegistry);
+      checkLastTagWas(0);
+      --recursionDepth;
+      popLimit(oldLimit);
+    }
+
+
+    @Override
+    public <T extends MessageLite> T readMessage(
+        final Parser<T> parser, final ExtensionRegistryLite extensionRegistry) throws IOException {
+      int length = readRawVarint32();
+      if (recursionDepth >= recursionLimit) {
+        throw InvalidProtocolBufferException.recursionLimitExceeded();
+      }
+      final int oldLimit = pushLimit(length);
+      ++recursionDepth;
+      T result = parser.parsePartialFrom(this, extensionRegistry);
+      checkLastTagWas(0);
+      --recursionDepth;
+      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 < FIXED_32_SIZE) {
+        throw InvalidProtocolBufferException.truncatedMessage();
+      }
+
+      final byte[] buffer = this.buffer;
+      pos = tempPos + FIXED_32_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 < FIXED_64_SIZE) {
+        throw InvalidProtocolBufferException.truncatedMessage();
+      }
+
+      final byte[] buffer = this.buffer;
+      pos = tempPos + FIXED_64_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();
+      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(FIXED_64_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(FIXED_32_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.writeRawVarint32(tag);
+            output.writeUInt64NoTag(value);
+            return true;
+          }
+        case WireFormat.WIRETYPE_FIXED64:
+          {
+            long value = readRawLittleEndian64();
+            output.writeRawVarint32(tag);
+            output.writeFixed64NoTag(value);
+            return true;
+          }
+        case WireFormat.WIRETYPE_LENGTH_DELIMITED:
+          {
+            ByteString value = readBytes();
+            output.writeRawVarint32(tag);
+            output.writeBytesNoTag(value);
+            return true;
+          }
+        case WireFormat.WIRETYPE_START_GROUP:
+          {
+            output.writeRawVarint32(tag);
+            skipMessage(output);
+            int endtag =
+                WireFormat.makeTag(
+                    WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP);
+            checkLastTagWas(endtag);
+            output.writeRawVarint32(endtag);
+            return true;
+          }
+        case WireFormat.WIRETYPE_END_GROUP:
+          {
+            return false;
+          }
+        case WireFormat.WIRETYPE_FIXED32:
+          {
+            int value = readRawLittleEndian32();
+            output.writeRawVarint32(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?
+        byte[] bytes = copyToArray(pos, pos + 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()) {
+        // TODO(nathanmittler): Is there a way to avoid this copy?
+        byte[] bytes = copyToArray(pos, pos + size);
+        // TODO(martinrb): We could save a pass by validating while decoding.
+        if (!Utf8.isValidUtf8(bytes)) {
+          throw InvalidProtocolBufferException.invalidUtf8();
+        }
+
+        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 void readGroup(
+        final int fieldNumber,
+        final MessageLite.Builder builder,
+        final ExtensionRegistryLite extensionRegistry)
+        throws IOException {
+      if (recursionDepth >= recursionLimit) {
+        throw InvalidProtocolBufferException.recursionLimitExceeded();
+      }
+      ++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 {
+      if (recursionDepth >= recursionLimit) {
+        throw InvalidProtocolBufferException.recursionLimitExceeded();
+      }
+      ++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();
+      if (recursionDepth >= recursionLimit) {
+        throw InvalidProtocolBufferException.recursionLimitExceeded();
+      }
+      final int oldLimit = pushLimit(length);
+      ++recursionDepth;
+      builder.mergeFrom(this, extensionRegistry);
+      checkLastTagWas(0);
+      --recursionDepth;
+      popLimit(oldLimit);
+    }
+
+
+    @Override
+    public <T extends MessageLite> T readMessage(
+        final Parser<T> parser, final ExtensionRegistryLite extensionRegistry) throws IOException {
+      int length = readRawVarint32();
+      if (recursionDepth >= recursionLimit) {
+        throw InvalidProtocolBufferException.recursionLimitExceeded();
+      }
+      final int oldLimit = pushLimit(length);
+      ++recursionDepth;
+      T result = parser.parsePartialFrom(this, extensionRegistry);
+      checkLastTagWas(0);
+      --recursionDepth;
+      popLimit(oldLimit);
+      return result;
+    }
+
+    @Override
+    public ByteString readBytes() throws IOException {
+      final int size = readRawVarint32();
+      if (size > 0 && size <= remaining()) {
+        ByteBuffer result;
+        if (immutable && enableAliasing) {
+          result = slice(pos, pos + size);
+        } else {
+          result = copy(pos, pos + size);
+        }
+        pos += size;
+        return ByteString.wrap(result);
+      }
+
+      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()) {
+        ByteBuffer result;
+        // "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) {
+          result = slice(pos, pos + size);
+        } else {
+          result = copy(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:
+      {
+        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 < FIXED_32_SIZE) {
+        throw InvalidProtocolBufferException.truncatedMessage();
+      }
+
+      pos = tempPos + FIXED_32_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 < FIXED_64_SIZE) {
+        throw InvalidProtocolBufferException.truncatedMessage();
+      }
+
+      pos = tempPos + FIXED_64_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();
+      try {
+        buffer.position(bufferPos(begin));
+        buffer.limit(bufferPos(end));
+        return buffer.slice();
+      } catch (IllegalArgumentException e) {
+        throw InvalidProtocolBufferException.truncatedMessage();
+      } finally {
+        buffer.position(prevPos);
+        buffer.limit(prevLimit);
+      }
+    }
+
+    private ByteBuffer copy(long begin, long end) throws IOException {
+      return ByteBuffer.wrap(copyToArray(begin, end));
+    }
+
+    private byte[] copyToArray(long begin, long end) throws IOException {
+      int prevPos = buffer.position();
+      int prevLimit = buffer.limit();
+      try {
+        buffer.position(bufferPos(begin));
+        buffer.limit(bufferPos(end));
+        byte[] bytes = new byte[(int) (end - begin)];
+        buffer.get(bytes);
+        return bytes;
+      } catch (IllegalArgumentException e) {
+        throw InvalidProtocolBufferException.truncatedMessage();
+      } finally {
+        buffer.position(prevPos);
+        buffer.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;
+    }
+
+    @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(FIXED_64_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(FIXED_32_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.writeRawVarint32(tag);
+            output.writeUInt64NoTag(value);
+            return true;
+          }
+        case WireFormat.WIRETYPE_FIXED64:
+          {
+            long value = readRawLittleEndian64();
+            output.writeRawVarint32(tag);
+            output.writeFixed64NoTag(value);
+            return true;
+          }
+        case WireFormat.WIRETYPE_LENGTH_DELIMITED:
+          {
+            ByteString value = readBytes();
+            output.writeRawVarint32(tag);
+            output.writeBytesNoTag(value);
+            return true;
+          }
+        case WireFormat.WIRETYPE_START_GROUP:
+          {
+            output.writeRawVarint32(tag);
+            skipMessage(output);
+            int endtag =
+                WireFormat.makeTag(
+                    WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP);
+            checkLastTagWas(endtag);
+            output.writeRawVarint32(endtag);
+            return true;
+          }
+        case WireFormat.WIRETYPE_END_GROUP:
+          {
+            return false;
+          }
+        case WireFormat.WIRETYPE_FIXED32:
+          {
+            int value = readRawLittleEndian32();
+            output.writeRawVarint32(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), 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);
+        tempPos = 0;
+      }
+      // TODO(martinrb): We could save a pass by validating while decoding.
+      if (!Utf8.isValidUtf8(bytes, tempPos, tempPos + size)) {
+        throw InvalidProtocolBufferException.invalidUtf8();
+      }
+      return new String(bytes, tempPos, size, UTF_8);
+    }
+
+    @Override
+    public void readGroup(
+        final int fieldNumber,
+        final MessageLite.Builder builder,
+        final ExtensionRegistryLite extensionRegistry)
+        throws IOException {
+      if (recursionDepth >= recursionLimit) {
+        throw InvalidProtocolBufferException.recursionLimitExceeded();
+      }
+      ++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 {
+      if (recursionDepth >= recursionLimit) {
+        throw InvalidProtocolBufferException.recursionLimitExceeded();
+      }
+      ++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();
+      if (recursionDepth >= recursionLimit) {
+        throw InvalidProtocolBufferException.recursionLimitExceeded();
+      }
+      final int oldLimit = pushLimit(length);
+      ++recursionDepth;
+      builder.mergeFrom(this, extensionRegistry);
+      checkLastTagWas(0);
+      --recursionDepth;
+      popLimit(oldLimit);
+    }
+
+
+    @Override
+    public <T extends MessageLite> T readMessage(
+        final Parser<T> parser, final ExtensionRegistryLite extensionRegistry) throws IOException {
+      int length = readRawVarint32();
+      if (recursionDepth >= recursionLimit) {
+        throw InvalidProtocolBufferException.recursionLimitExceeded();
+      }
+      final int oldLimit = pushLimit(length);
+      ++recursionDepth;
+      T result = parser.parsePartialFrom(this, extensionRegistry);
+      checkLastTagWas(0);
+      --recursionDepth;
+      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;
+      }
+      // Slow path:  Build a byte array first then copy it.
+      return ByteString.wrap(readRawBytesSlowPath(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.
+        return readRawBytesSlowPath(size);
+      }
+    }
+
+    @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.
+      return ByteBuffer.wrap(readRawBytesSlowPath(size));
+    }
+
+    @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 < FIXED_32_SIZE) {
+        refillBuffer(FIXED_32_SIZE);
+        tempPos = pos;
+      }
+
+      final byte[] buffer = this.buffer;
+      pos = tempPos + FIXED_32_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 < FIXED_64_SIZE) {
+        refillBuffer(FIXED_64_SIZE);
+        tempPos = pos;
+      }
+
+      final byte[] buffer = this.buffer;
+      pos = tempPos + FIXED_64_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

<TRUNCATED>

Mime
View raw message