// 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, InternalNano.UTF_8); bufferPos += size; return result; } else { // Slow path: Build a byte array first then copy it. return new String(readRawBytes(size), InternalNano.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 if (size == 0) { return WireFormatNano.EMPTY_BYTES; } 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); } } }