content: // Protocol Buffers - Google's data interchange format
  // Copyright 2013 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.nano;
  
  import java.io.IOException;
  
  /**
   * Reads and decodes protocol message fields.
   *
   * This class contains two kinds of methods:  methods that read specific
   * protocol message constructs and field types (e.g. {@link #readTag()} and
   * {@link #readInt32()}) and methods that read low-level values (e.g.
   * {@link #readRawVarint32()} and {@link #readRawBytes}).  If you are reading
   * encoded protocol messages, you should use the former methods, but if you are
   * reading some other format of your own design, use the latter.
   *
   * @author kenton@google.com Kenton Varda
   */
  public final class CodedInputByteBufferNano {
    /**
     * Create a new CodedInputStream wrapping the given byte array.
     */
    public static CodedInputByteBufferNano newInstance(final byte[] buf) {
      return newInstance(buf, 0, buf.length);
    }
  
    /**
     * Create a new CodedInputStream wrapping the given byte array slice.
     */
    public static CodedInputByteBufferNano newInstance(final byte[] buf, final int off,
                                               final int len) {
      return new CodedInputByteBufferNano(buf, off, len);
    }
  
    // -----------------------------------------------------------------
  
    /**
     * 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 int readTag() throws IOException {
      if (isAtEnd()) {
        lastTag = 0;
        return 0;
      }
  
      lastTag = readRawVarint32();
      if (lastTag == 0) {
        // If we actually read zero, that's not a valid tag.
        throw InvalidProtocolBufferNanoException.invalidTag();
      }
      return lastTag;
    }
  
    /**
     * 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 InvalidProtocolBufferNanoException {@code value} does not match the
     *                                        last tag.
     */
    public void checkLastTagWas(final int value)
                                throws InvalidProtocolBufferNanoException {
      if (lastTag != value) {
        throw InvalidProtocolBufferNanoException.invalidEndTag();
      }
    }
  
    /**
     * 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 boolean skipField(final int tag) throws IOException {
      switch (WireFormatNano.getTagWireType(tag)) {
        case WireFormatNano.WIRETYPE_VARINT:
          readInt32();
          return true;
        case WireFormatNano.WIRETYPE_FIXED64:
          readRawLittleEndian64();
          return true;
        case WireFormatNano.WIRETYPE_LENGTH_DELIMITED:
          skipRawBytes(readRawVarint32());
          return true;
        case WireFormatNano.WIRETYPE_START_GROUP:
          skipMessage();
          checkLastTagWas(
            WireFormatNano.makeTag(WireFormatNano.getTagFieldNumber(tag),
                               WireFormatNano.WIRETYPE_END_GROUP));
          return true;
        case WireFormatNano.WIRETYPE_END_GROUP:
          return false;
        case WireFormatNano.WIRETYPE_FIXED32:
          readRawLittleEndian32();
          return true;
        default:
          throw InvalidProtocolBufferNanoException.invalidWireType();
      }
    }
  
    /**
     * Reads and discards an entire message.  This will read either until EOF
     * or until an endgroup tag, whichever comes first.
     */
    public void skipMessage() throws IOException {
      while (true) {
        final int tag = readTag();
        if (tag == 0 || !skipField(tag)) {
          return;
        }
      }
    }
  
    // -----------------------------------------------------------------
  
    /** Read a {@code double} field value from the stream. */
    public double readDouble() throws IOException {
      return Double.longBitsToDouble(readRawLittleEndian64());
    }
  
    /** Read a {@code float} field value from the stream. */
    public float readFloat() throws IOException {
      return Float.intBitsToFloat(readRawLittleEndian32());
    }
  
    /** Read a {@code uint64} field value from the stream. */
    public long readUInt64() throws IOException {
      return readRawVarint64();
    }
  
    /** Read an {@code int64} field value from the stream. */
    public long readInt64() throws IOException {
      return readRawVarint64();
    }
  
    /** Read an {@code int32} field value from the stream. */
    public int readInt32() throws IOException {
      return readRawVarint32();
    }
  
    /** Read a {@code fixed64} field value from the stream. */
    public long readFixed64() throws IOException {
      return readRawLittleEndian64();
    }
  
    /** Read a {@code fixed32} field value from the stream. */
    public int readFixed32() throws IOException {
      return readRawLittleEndian32();
    }
  
    /** Read a {@code bool} field value from the stream. */
    public boolean readBool() throws IOException {
      return readRawVarint32() != 0;
    }
  
    /** Read a {@code string} field value from the stream. */
    public String readString() throws IOException {
      final int size = readRawVarint32();
      if (size <= (bufferSize - bufferPos) && size > 0) {
        // Fast path:  We already have the bytes in a contiguous buffer, so
        //   just copy directly from it.
        final String result = new String(buffer, bufferPos, size, "UTF-8");
        bufferPos += size;
        return result;
      } else {
        // Slow path:  Build a byte array first then copy it.
        return new String(readRawBytes(size), "UTF-8");
      }
    }
  
    /** Read a {@code group} field value from the stream. */
    public void readGroup(final MessageNano msg, final int fieldNumber)
        throws IOException {
      if (recursionDepth >= recursionLimit) {
        throw InvalidProtocolBufferNanoException.recursionLimitExceeded();
      }
      ++recursionDepth;
      msg.mergeFrom(this);
      checkLastTagWas(
        WireFormatNano.makeTag(fieldNumber, WireFormatNano.WIRETYPE_END_GROUP));
      --recursionDepth;
    }
  
    public void readMessage(final MessageNano msg)
        throws IOException {
      final int length = readRawVarint32();
      if (recursionDepth >= recursionLimit) {
        throw InvalidProtocolBufferNanoException.recursionLimitExceeded();
      }
      final int oldLimit = pushLimit(length);
      ++recursionDepth;
      msg.mergeFrom(this);
      checkLastTagWas(0);
      --recursionDepth;
      popLimit(oldLimit);
    }
  
    /** Read a {@code bytes} field value from the stream. */
    public byte[] readBytes() throws IOException {
      final int size = readRawVarint32();
      if (size <= (bufferSize - bufferPos) && size > 0) {
        // Fast path:  We already have the bytes in a contiguous buffer, so
        //   just copy directly from it.
        final byte[] result = new byte[size];
        System.arraycopy(buffer, bufferPos, result, 0, size);
        bufferPos += size;
        return result;
      } else {
        // Slow path:  Build a byte array first then copy it.
        return readRawBytes(size);
      }
    }
  
    /** Read a {@code uint32} field value from the stream. */
    public int readUInt32() throws IOException {
      return readRawVarint32();
    }
  
    /**
     * Read an enum field value from the stream.  Caller is responsible
     * for converting the numeric value to an actual enum.
     */
    public int readEnum() throws IOException {
      return readRawVarint32();
    }
  
    /** Read an {@code sfixed32} field value from the stream. */
    public int readSFixed32() throws IOException {
      return readRawLittleEndian32();
    }
  
    /** Read an {@code sfixed64} field value from the stream. */
    public long readSFixed64() throws IOException {
      return readRawLittleEndian64();
    }
  
    /** Read an {@code sint32} field value from the stream. */
    public int readSInt32() throws IOException {
      return decodeZigZag32(readRawVarint32());
    }
  
    /** Read an {@code sint64} field value from the stream. */
    public long readSInt64() throws IOException {
      return decodeZigZag64(readRawVarint64());
    }
  
    // =================================================================
  
    /**
     * Read a raw Varint from the stream.  If larger than 32 bits, discard the
     * upper bits.
     */
    public int readRawVarint32() throws IOException {
      byte tmp = readRawByte();
      if (tmp >= 0) {
        return tmp;
      }
      int result = tmp & 0x7f;
      if ((tmp = readRawByte()) >= 0) {
        result |= tmp << 7;
      } else {
        result |= (tmp & 0x7f) << 7;
        if ((tmp = readRawByte()) >= 0) {
          result |= tmp << 14;
        } else {
          result |= (tmp & 0x7f) << 14;
          if ((tmp = readRawByte()) >= 0) {
            result |= tmp << 21;
          } else {
            result |= (tmp & 0x7f) << 21;
            result |= (tmp = readRawByte()) << 28;
            if (tmp < 0) {
              // Discard upper 32 bits.
              for (int i = 0; i < 5; i++) {
                if (readRawByte() >= 0) {
                  return result;
                }
              }
              throw InvalidProtocolBufferNanoException.malformedVarint();
            }
          }
        }
      }
      return result;
    }
  
    /** Read a raw Varint from the stream. */
    public long readRawVarint64() throws IOException {
      int shift = 0;
      long result = 0;
      while (shift < 64) {
        final byte b = readRawByte();
        result |= (long)(b & 0x7F) << shift;
        if ((b & 0x80) == 0) {
          return result;
        }
        shift += 7;
      }
      throw InvalidProtocolBufferNanoException.malformedVarint();
    }
  
    /** Read a 32-bit little-endian integer from the stream. */
    public int readRawLittleEndian32() throws IOException {
      final byte b1 = readRawByte();
      final byte b2 = readRawByte();
      final byte b3 = readRawByte();
      final byte b4 = readRawByte();
      return ((b1 & 0xff)      ) |
             ((b2 & 0xff) <<  8) |
             ((b3 & 0xff) << 16) |
             ((b4 & 0xff) << 24);
    }
  
    /** Read a 64-bit little-endian integer from the stream. */
    public long readRawLittleEndian64() throws IOException {
      final byte b1 = readRawByte();
      final byte b2 = readRawByte();
      final byte b3 = readRawByte();
      final byte b4 = readRawByte();
      final byte b5 = readRawByte();
      final byte b6 = readRawByte();
      final byte b7 = readRawByte();
      final byte b8 = readRawByte();
      return (((long)b1 & 0xff)      ) |
             (((long)b2 & 0xff) <<  8) |
             (((long)b3 & 0xff) << 16) |
             (((long)b4 & 0xff) << 24) |
             (((long)b5 & 0xff) << 32) |
             (((long)b6 & 0xff) << 40) |
             (((long)b7 & 0xff) << 48) |
             (((long)b8 & 0xff) << 56);
    }
  
    /**
     * 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);
    }
  
    // -----------------------------------------------------------------
  
    private final byte[] buffer;
    private int bufferStart;
    private int bufferSize;
    private int bufferSizeAfterLimit;
    private int bufferPos;
    private int lastTag;
  
    /** The absolute position of the end of the current message. */
    private int currentLimit = Integer.MAX_VALUE;
  
    /** See setRecursionLimit() */
    private int recursionDepth;
    private int recursionLimit = DEFAULT_RECURSION_LIMIT;
  
    /** See setSizeLimit() */
    private int sizeLimit = DEFAULT_SIZE_LIMIT;
  
    private static final int DEFAULT_RECURSION_LIMIT = 64;
    private static final int DEFAULT_SIZE_LIMIT = 64 << 20;  // 64MB
  
    private CodedInputByteBufferNano(final byte[] buffer, final int off, final int len) {
      this.buffer = buffer;
      bufferStart = off;
      bufferSize = off + len;
      bufferPos = off;
    }
  
    /**
     * 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 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;
    }
  
    /**
     * 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.
     * <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 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)}).
     */
    public void resetSizeCounter() {
    }
  
    /**
     * Sets {@code currentLimit} to (current position) + {@code byteLimit}.  This
     * is called when descending into a length-delimited embedded message.
     *
     * @return the old limit.
     */
    public int pushLimit(int byteLimit) throws InvalidProtocolBufferNanoException {
      if (byteLimit < 0) {
        throw InvalidProtocolBufferNanoException.negativeSize();
      }
      byteLimit += bufferPos;
      final int oldLimit = currentLimit;
      if (byteLimit > oldLimit) {
        throw InvalidProtocolBufferNanoException.truncatedMessage();
      }
      currentLimit = byteLimit;
  
      recomputeBufferSizeAfterLimit();
  
      return oldLimit;
    }
  
    private void recomputeBufferSizeAfterLimit() {
      bufferSize += bufferSizeAfterLimit;
      final int bufferEnd = bufferSize;
      if (bufferEnd > currentLimit) {
        // Limit is in current buffer.
        bufferSizeAfterLimit = bufferEnd - currentLimit;
        bufferSize -= bufferSizeAfterLimit;
      } else {
        bufferSizeAfterLimit = 0;
      }
    }
  
    /**
     * Discards the current limit, returning to the previous limit.
     *
     * @param oldLimit The old limit, as returned by {@code pushLimit}.
     */
    public void popLimit(final int oldLimit) {
      currentLimit = oldLimit;
      recomputeBufferSizeAfterLimit();
    }
  
    /**
     * Returns the number of bytes to be read before the current limit.
     * If no limit is set, returns -1.
     */
    public int getBytesUntilLimit() {
      if (currentLimit == Integer.MAX_VALUE) {
        return -1;
      }
  
      final int currentAbsolutePosition = bufferPos;
      return currentLimit - currentAbsolutePosition;
    }
  
    /**
     * 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 boolean isAtEnd() {
      return bufferPos == bufferSize;
    }
  
    /**
     * Get current position in buffer relative to beginning offset.
     */
    public int getPosition() {
      return bufferPos - bufferStart;
    }
  
    /**
     * Retrieves a subset of data in the buffer. The returned array is not backed by the original
     * buffer array.
     *
     * @param offset the position (relative to the buffer start position) to start at.
     * @param length the number of bytes to retrieve.
     */
    public byte[] getData(int offset, int length) {
      if (length == 0) {
        return WireFormatNano.EMPTY_BYTES;
      }
      byte[] copy = new byte[length];
      int start = bufferStart + offset;
      System.arraycopy(buffer, start, copy, 0, length);
      return copy;
    }
  
    /**
     * Rewind to previous position. Cannot go forward.
     */
    public void rewindToPosition(int position) {
      if (position > bufferPos - bufferStart) {
        throw new IllegalArgumentException(
                "Position " + position + " is beyond current " + (bufferPos - bufferStart));
      }
      if (position < 0) {
        throw new IllegalArgumentException("Bad position " + position);
      }
      bufferPos = bufferStart + position;
    }
  
    /**
     * Read one byte from the input.
     *
     * @throws InvalidProtocolBufferNanoException The end of the stream or the current
     *                                        limit was reached.
     */
    public byte readRawByte() throws IOException {
      if (bufferPos == bufferSize) {
        throw InvalidProtocolBufferNanoException.truncatedMessage();
      }
      return buffer[bufferPos++];
    }
  
    /**
     * Read a fixed size of bytes from the input.
     *
     * @throws InvalidProtocolBufferNanoException The end of the stream or the current
     *                                        limit was reached.
     */
    public byte[] readRawBytes(final int size) throws IOException {
      if (size < 0) {
        throw InvalidProtocolBufferNanoException.negativeSize();
      }
  
      if (bufferPos + size > currentLimit) {
        // Read to the end of the stream anyway.
        skipRawBytes(currentLimit - bufferPos);
        // Then fail.
        throw InvalidProtocolBufferNanoException.truncatedMessage();
      }
  
      if (size <= bufferSize - bufferPos) {
        // We have all the bytes we need already.
        final byte[] bytes = new byte[size];
        System.arraycopy(buffer, bufferPos, bytes, 0, size);
        bufferPos += size;
        return bytes;
      } else {
        throw InvalidProtocolBufferNanoException.truncatedMessage();
      }
    }
  
    /**
     * Reads and discards {@code size} bytes.
     *
     * @throws InvalidProtocolBufferNanoException The end of the stream or the current
     *                                        limit was reached.
     */
    public void skipRawBytes(final int size) throws IOException {
      if (size < 0) {
        throw InvalidProtocolBufferNanoException.negativeSize();
      }
  
      if (bufferPos + size > currentLimit) {
        // Read to the end of the stream anyway.
        skipRawBytes(currentLimit - bufferPos);
        // Then fail.
        throw InvalidProtocolBufferNanoException.truncatedMessage();
      }
  
      if (size <= bufferSize - bufferPos) {
        // We have all the bytes we need already.
        bufferPos += size;
      } else {
        throw InvalidProtocolBufferNanoException.truncatedMessage();
      }
    }
  
    // Read a primitive type.
    Object readPrimitiveField(int type) throws IOException {
      switch (type) {
        case InternalNano.TYPE_DOUBLE:
            return readDouble();
        case InternalNano.TYPE_FLOAT:
            return readFloat();
        case InternalNano.TYPE_INT64:
            return readInt64();
        case InternalNano.TYPE_UINT64:
            return readUInt64();
        case InternalNano.TYPE_INT32:
            return readInt32();
        case InternalNano.TYPE_FIXED64:
            return readFixed64();
        case InternalNano.TYPE_FIXED32:
            return readFixed32();
        case InternalNano.TYPE_BOOL:
            return readBool();
        case InternalNano.TYPE_STRING:
            return readString();
        case InternalNano.TYPE_BYTES:
            return readBytes();
        case InternalNano.TYPE_UINT32:
            return readUInt32();
        case InternalNano.TYPE_ENUM:
            return readEnum();
        case InternalNano.TYPE_SFIXED32:
            return readSFixed32();
        case InternalNano.TYPE_SFIXED64:
            return readSFixed64();
        case InternalNano.TYPE_SINT32:
            return readSInt32();
        case InternalNano.TYPE_SINT64:
            return readSInt64();
        default:
            throw new IllegalArgumentException("Unknown type " + type);
      }
    }
  }
  