Home | History | Annotate | Download | only in micro
      1 // Protocol Buffers - Google's data interchange format
      2 // Copyright 2008 Google Inc.  All rights reserved.
      3 // http://code.google.com/p/protobuf/
      4 //
      5 // Redistribution and use in source and binary forms, with or without
      6 // modification, are permitted provided that the following conditions are
      7 // met:
      8 //
      9 //     * Redistributions of source code must retain the above copyright
     10 // notice, this list of conditions and the following disclaimer.
     11 //     * Redistributions in binary form must reproduce the above
     12 // copyright notice, this list of conditions and the following disclaimer
     13 // in the documentation and/or other materials provided with the
     14 // distribution.
     15 //     * Neither the name of Google Inc. nor the names of its
     16 // contributors may be used to endorse or promote products derived from
     17 // this software without specific prior written permission.
     18 //
     19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30 
     31 package com.google.protobuf.micro;
     32 
     33 import java.io.IOException;
     34 import java.io.InputStream;
     35 
     36 /**
     37  * Reads and decodes protocol message fields.
     38  *
     39  * This class contains two kinds of methods:  methods that read specific
     40  * protocol message constructs and field types (e.g. {@link #readTag()} and
     41  * {@link #readInt32()}) and methods that read low-level values (e.g.
     42  * {@link #readRawVarint32()} and {@link #readRawBytes}).  If you are reading
     43  * encoded protocol messages, you should use the former methods, but if you are
     44  * reading some other format of your own design, use the latter.
     45  *
     46  * @author kenton (at) google.com Kenton Varda
     47  */
     48 public final class CodedInputStreamMicro {
     49   /**
     50    * Create a new CodedInputStream wrapping the given InputStream.
     51    */
     52   public static CodedInputStreamMicro newInstance(final InputStream input) {
     53     return new CodedInputStreamMicro(input);
     54   }
     55 
     56   /**
     57    * Create a new CodedInputStream wrapping the given byte array.
     58    */
     59   public static CodedInputStreamMicro newInstance(final byte[] buf) {
     60     return newInstance(buf, 0, buf.length);
     61   }
     62 
     63   /**
     64    * Create a new CodedInputStream wrapping the given byte array slice.
     65    */
     66   public static CodedInputStreamMicro newInstance(final byte[] buf, final int off,
     67                                              final int len) {
     68     return new CodedInputStreamMicro(buf, off, len);
     69   }
     70 
     71   // -----------------------------------------------------------------
     72 
     73   /**
     74    * Attempt to read a field tag, returning zero if we have reached EOF.
     75    * Protocol message parsers use this to read tags, since a protocol message
     76    * may legally end wherever a tag occurs, and zero is not a valid tag number.
     77    */
     78   public int readTag() throws IOException {
     79     if (isAtEnd()) {
     80       lastTag = 0;
     81       return 0;
     82     }
     83 
     84     lastTag = readRawVarint32();
     85     if (lastTag == 0) {
     86       // If we actually read zero, that's not a valid tag.
     87       throw InvalidProtocolBufferMicroException.invalidTag();
     88     }
     89     return lastTag;
     90   }
     91 
     92   /**
     93    * Verifies that the last call to readTag() returned the given tag value.
     94    * This is used to verify that a nested group ended with the correct
     95    * end tag.
     96    *
     97    * @throws InvalidProtocolBufferMicroException {@code value} does not match the
     98    *                                        last tag.
     99    */
    100   public void checkLastTagWas(final int value)
    101                               throws InvalidProtocolBufferMicroException {
    102     if (lastTag != value) {
    103       throw InvalidProtocolBufferMicroException.invalidEndTag();
    104     }
    105   }
    106 
    107   /**
    108    * Reads and discards a single field, given its tag value.
    109    *
    110    * @return {@code false} if the tag is an endgroup tag, in which case
    111    *         nothing is skipped.  Otherwise, returns {@code true}.
    112    */
    113   public boolean skipField(final int tag) throws IOException {
    114     switch (WireFormatMicro.getTagWireType(tag)) {
    115       case WireFormatMicro.WIRETYPE_VARINT:
    116         readInt32();
    117         return true;
    118       case WireFormatMicro.WIRETYPE_FIXED64:
    119         readRawLittleEndian64();
    120         return true;
    121       case WireFormatMicro.WIRETYPE_LENGTH_DELIMITED:
    122         skipRawBytes(readRawVarint32());
    123         return true;
    124       case WireFormatMicro.WIRETYPE_START_GROUP:
    125         skipMessage();
    126         checkLastTagWas(
    127           WireFormatMicro.makeTag(WireFormatMicro.getTagFieldNumber(tag),
    128                              WireFormatMicro.WIRETYPE_END_GROUP));
    129         return true;
    130       case WireFormatMicro.WIRETYPE_END_GROUP:
    131         return false;
    132       case WireFormatMicro.WIRETYPE_FIXED32:
    133         readRawLittleEndian32();
    134         return true;
    135       default:
    136         throw InvalidProtocolBufferMicroException.invalidWireType();
    137     }
    138   }
    139 
    140   /**
    141    * Reads and discards an entire message.  This will read either until EOF
    142    * or until an endgroup tag, whichever comes first.
    143    */
    144   public void skipMessage() throws IOException {
    145     while (true) {
    146       final int tag = readTag();
    147       if (tag == 0 || !skipField(tag)) {
    148         return;
    149       }
    150     }
    151   }
    152 
    153   // -----------------------------------------------------------------
    154 
    155   /** Read a {@code double} field value from the stream. */
    156   public double readDouble() throws IOException {
    157     return Double.longBitsToDouble(readRawLittleEndian64());
    158   }
    159 
    160   /** Read a {@code float} field value from the stream. */
    161   public float readFloat() throws IOException {
    162     return Float.intBitsToFloat(readRawLittleEndian32());
    163   }
    164 
    165   /** Read a {@code uint64} field value from the stream. */
    166   public long readUInt64() throws IOException {
    167     return readRawVarint64();
    168   }
    169 
    170   /** Read an {@code int64} field value from the stream. */
    171   public long readInt64() throws IOException {
    172     return readRawVarint64();
    173   }
    174 
    175   /** Read an {@code int32} field value from the stream. */
    176   public int readInt32() throws IOException {
    177     return readRawVarint32();
    178   }
    179 
    180   /** Read a {@code fixed64} field value from the stream. */
    181   public long readFixed64() throws IOException {
    182     return readRawLittleEndian64();
    183   }
    184 
    185   /** Read a {@code fixed32} field value from the stream. */
    186   public int readFixed32() throws IOException {
    187     return readRawLittleEndian32();
    188   }
    189 
    190   /** Read a {@code bool} field value from the stream. */
    191   public boolean readBool() throws IOException {
    192     return readRawVarint32() != 0;
    193   }
    194 
    195   /** Read a {@code string} field value from the stream. */
    196   public String readString() throws IOException {
    197     final int size = readRawVarint32();
    198     if (size <= (bufferSize - bufferPos) && size > 0) {
    199       // Fast path:  We already have the bytes in a contiguous buffer, so
    200       //   just copy directly from it.
    201       final String result = new String(buffer, bufferPos, size, "UTF-8");
    202       bufferPos += size;
    203       return result;
    204     } else {
    205       // Slow path:  Build a byte array first then copy it.
    206       return new String(readRawBytes(size), "UTF-8");
    207     }
    208   }
    209 
    210   /** Read a {@code group} field value from the stream. */
    211   public void readGroup(final MessageMicro msg, final int fieldNumber)
    212       throws IOException {
    213     if (recursionDepth >= recursionLimit) {
    214       throw InvalidProtocolBufferMicroException.recursionLimitExceeded();
    215     }
    216     ++recursionDepth;
    217     msg.mergeFrom(this);
    218     checkLastTagWas(
    219       WireFormatMicro.makeTag(fieldNumber, WireFormatMicro.WIRETYPE_END_GROUP));
    220     --recursionDepth;
    221   }
    222 
    223   public void readMessage(final MessageMicro msg)
    224       throws IOException {
    225     final int length = readRawVarint32();
    226     if (recursionDepth >= recursionLimit) {
    227       throw InvalidProtocolBufferMicroException.recursionLimitExceeded();
    228     }
    229     final int oldLimit = pushLimit(length);
    230     ++recursionDepth;
    231     msg.mergeFrom(this);
    232     checkLastTagWas(0);
    233     --recursionDepth;
    234     popLimit(oldLimit);
    235   }
    236 
    237   /** Read a {@code bytes} field value from the stream. */
    238   public ByteStringMicro readBytes() throws IOException {
    239     final int size = readRawVarint32();
    240     if (size <= (bufferSize - bufferPos) && size > 0) {
    241       // Fast path:  We already have the bytes in a contiguous buffer, so
    242       //   just copy directly from it.
    243       final ByteStringMicro result = ByteStringMicro.copyFrom(buffer, bufferPos, size);
    244       bufferPos += size;
    245       return result;
    246     } else if (size == 0) {
    247       return ByteStringMicro.EMPTY;
    248     } else {
    249       // Slow path:  Build a byte array first then copy it.
    250       return ByteStringMicro.copyFrom(readRawBytes(size));
    251     }
    252   }
    253 
    254   /** Read a {@code uint32} field value from the stream. */
    255   public int readUInt32() throws IOException {
    256     return readRawVarint32();
    257   }
    258 
    259   /**
    260    * Read an enum field value from the stream.  Caller is responsible
    261    * for converting the numeric value to an actual enum.
    262    */
    263   public int readEnum() throws IOException {
    264     return readRawVarint32();
    265   }
    266 
    267   /** Read an {@code sfixed32} field value from the stream. */
    268   public int readSFixed32() throws IOException {
    269     return readRawLittleEndian32();
    270   }
    271 
    272   /** Read an {@code sfixed64} field value from the stream. */
    273   public long readSFixed64() throws IOException {
    274     return readRawLittleEndian64();
    275   }
    276 
    277   /** Read an {@code sint32} field value from the stream. */
    278   public int readSInt32() throws IOException {
    279     return decodeZigZag32(readRawVarint32());
    280   }
    281 
    282   /** Read an {@code sint64} field value from the stream. */
    283   public long readSInt64() throws IOException {
    284     return decodeZigZag64(readRawVarint64());
    285   }
    286 
    287   // =================================================================
    288 
    289   /**
    290    * Read a raw Varint from the stream.  If larger than 32 bits, discard the
    291    * upper bits.
    292    */
    293   public int readRawVarint32() throws IOException {
    294     byte tmp = readRawByte();
    295     if (tmp >= 0) {
    296       return tmp;
    297     }
    298     int result = tmp & 0x7f;
    299     if ((tmp = readRawByte()) >= 0) {
    300       result |= tmp << 7;
    301     } else {
    302       result |= (tmp & 0x7f) << 7;
    303       if ((tmp = readRawByte()) >= 0) {
    304         result |= tmp << 14;
    305       } else {
    306         result |= (tmp & 0x7f) << 14;
    307         if ((tmp = readRawByte()) >= 0) {
    308           result |= tmp << 21;
    309         } else {
    310           result |= (tmp & 0x7f) << 21;
    311           result |= (tmp = readRawByte()) << 28;
    312           if (tmp < 0) {
    313             // Discard upper 32 bits.
    314             for (int i = 0; i < 5; i++) {
    315               if (readRawByte() >= 0) {
    316                 return result;
    317               }
    318             }
    319             throw InvalidProtocolBufferMicroException.malformedVarint();
    320           }
    321         }
    322       }
    323     }
    324     return result;
    325   }
    326 
    327   /**
    328    * Reads a varint from the input one byte at a time, so that it does not
    329    * read any bytes after the end of the varint.  If you simply wrapped the
    330    * stream in a CodedInputStream and used {@link #readRawVarint32(InputStream)}
    331    * then you would probably end up reading past the end of the varint since
    332    * CodedInputStream buffers its input.
    333    */
    334   static int readRawVarint32(final InputStream input) throws IOException {
    335     int result = 0;
    336     int offset = 0;
    337     for (; offset < 32; offset += 7) {
    338       final int b = input.read();
    339       if (b == -1) {
    340         throw InvalidProtocolBufferMicroException.truncatedMessage();
    341       }
    342       result |= (b & 0x7f) << offset;
    343       if ((b & 0x80) == 0) {
    344         return result;
    345       }
    346     }
    347     // Keep reading up to 64 bits.
    348     for (; offset < 64; offset += 7) {
    349       final int b = input.read();
    350       if (b == -1) {
    351         throw InvalidProtocolBufferMicroException.truncatedMessage();
    352       }
    353       if ((b & 0x80) == 0) {
    354         return result;
    355       }
    356     }
    357     throw InvalidProtocolBufferMicroException.malformedVarint();
    358   }
    359 
    360   /** Read a raw Varint from the stream. */
    361   public long readRawVarint64() throws IOException {
    362     int shift = 0;
    363     long result = 0;
    364     while (shift < 64) {
    365       final byte b = readRawByte();
    366       result |= (long)(b & 0x7F) << shift;
    367       if ((b & 0x80) == 0) {
    368         return result;
    369       }
    370       shift += 7;
    371     }
    372     throw InvalidProtocolBufferMicroException.malformedVarint();
    373   }
    374 
    375   /** Read a 32-bit little-endian integer from the stream. */
    376   public int readRawLittleEndian32() throws IOException {
    377     final byte b1 = readRawByte();
    378     final byte b2 = readRawByte();
    379     final byte b3 = readRawByte();
    380     final byte b4 = readRawByte();
    381     return ((b1 & 0xff)      ) |
    382            ((b2 & 0xff) <<  8) |
    383            ((b3 & 0xff) << 16) |
    384            ((b4 & 0xff) << 24);
    385   }
    386 
    387   /** Read a 64-bit little-endian integer from the stream. */
    388   public long readRawLittleEndian64() throws IOException {
    389     final byte b1 = readRawByte();
    390     final byte b2 = readRawByte();
    391     final byte b3 = readRawByte();
    392     final byte b4 = readRawByte();
    393     final byte b5 = readRawByte();
    394     final byte b6 = readRawByte();
    395     final byte b7 = readRawByte();
    396     final byte b8 = readRawByte();
    397     return (((long)b1 & 0xff)      ) |
    398            (((long)b2 & 0xff) <<  8) |
    399            (((long)b3 & 0xff) << 16) |
    400            (((long)b4 & 0xff) << 24) |
    401            (((long)b5 & 0xff) << 32) |
    402            (((long)b6 & 0xff) << 40) |
    403            (((long)b7 & 0xff) << 48) |
    404            (((long)b8 & 0xff) << 56);
    405   }
    406 
    407   /**
    408    * Decode a ZigZag-encoded 32-bit value.  ZigZag encodes signed integers
    409    * into values that can be efficiently encoded with varint.  (Otherwise,
    410    * negative values must be sign-extended to 64 bits to be varint encoded,
    411    * thus always taking 10 bytes on the wire.)
    412    *
    413    * @param n An unsigned 32-bit integer, stored in a signed int because
    414    *          Java has no explicit unsigned support.
    415    * @return A signed 32-bit integer.
    416    */
    417   public static int decodeZigZag32(final int n) {
    418     return (n >>> 1) ^ -(n & 1);
    419   }
    420 
    421   /**
    422    * Decode a ZigZag-encoded 64-bit value.  ZigZag encodes signed integers
    423    * into values that can be efficiently encoded with varint.  (Otherwise,
    424    * negative values must be sign-extended to 64 bits to be varint encoded,
    425    * thus always taking 10 bytes on the wire.)
    426    *
    427    * @param n An unsigned 64-bit integer, stored in a signed int because
    428    *          Java has no explicit unsigned support.
    429    * @return A signed 64-bit integer.
    430    */
    431   public static long decodeZigZag64(final long n) {
    432     return (n >>> 1) ^ -(n & 1);
    433   }
    434 
    435   // -----------------------------------------------------------------
    436 
    437   private final byte[] buffer;
    438   private int bufferSize;
    439   private int bufferSizeAfterLimit;
    440   private int bufferPos;
    441   private final InputStream input;
    442   private int lastTag;
    443 
    444   /**
    445    * The total number of bytes read before the current buffer.  The total
    446    * bytes read up to the current position can be computed as
    447    * {@code totalBytesRetired + bufferPos}.
    448    */
    449   private int totalBytesRetired;
    450 
    451   /** The absolute position of the end of the current message. */
    452   private int currentLimit = Integer.MAX_VALUE;
    453 
    454   /** See setRecursionLimit() */
    455   private int recursionDepth;
    456   private int recursionLimit = DEFAULT_RECURSION_LIMIT;
    457 
    458   /** See setSizeLimit() */
    459   private int sizeLimit = DEFAULT_SIZE_LIMIT;
    460 
    461   private static final int DEFAULT_RECURSION_LIMIT = 64;
    462   private static final int DEFAULT_SIZE_LIMIT = 64 << 20;  // 64MB
    463   private static final int BUFFER_SIZE = 4096;
    464 
    465   private CodedInputStreamMicro(final byte[] buffer, final int off, final int len) {
    466     this.buffer = buffer;
    467     bufferSize = off + len;
    468     bufferPos = off;
    469     input = null;
    470   }
    471 
    472   private CodedInputStreamMicro(final InputStream input) {
    473     buffer = new byte[BUFFER_SIZE];
    474     bufferSize = 0;
    475     bufferPos = 0;
    476     this.input = input;
    477   }
    478 
    479   /**
    480    * Set the maximum message recursion depth.  In order to prevent malicious
    481    * messages from causing stack overflows, {@code CodedInputStream} limits
    482    * how deeply messages may be nested.  The default limit is 64.
    483    *
    484    * @return the old limit.
    485    */
    486   public int setRecursionLimit(final int limit) {
    487     if (limit < 0) {
    488       throw new IllegalArgumentException(
    489         "Recursion limit cannot be negative: " + limit);
    490     }
    491     final int oldLimit = recursionLimit;
    492     recursionLimit = limit;
    493     return oldLimit;
    494   }
    495 
    496   /**
    497    * Set the maximum message size.  In order to prevent malicious
    498    * messages from exhausting memory or causing integer overflows,
    499    * {@code CodedInputStream} limits how large a message may be.
    500    * The default limit is 64MB.  You should set this limit as small
    501    * as you can without harming your app's functionality.  Note that
    502    * size limits only apply when reading from an {@code InputStream}, not
    503    * when constructed around a raw byte array (nor with
    504    * {@link ByteStringMicro#newCodedInput}).
    505    * <p>
    506    * If you want to read several messages from a single CodedInputStream, you
    507    * could call {@link #resetSizeCounter()} after each one to avoid hitting the
    508    * size limit.
    509    *
    510    * @return the old limit.
    511    */
    512   public int setSizeLimit(final int limit) {
    513     if (limit < 0) {
    514       throw new IllegalArgumentException(
    515         "Size limit cannot be negative: " + limit);
    516     }
    517     final int oldLimit = sizeLimit;
    518     sizeLimit = limit;
    519     return oldLimit;
    520   }
    521 
    522   /**
    523    * Resets the current size counter to zero (see {@link #setSizeLimit(int)}).
    524    */
    525   public void resetSizeCounter() {
    526     totalBytesRetired = 0;
    527   }
    528 
    529   /**
    530    * Sets {@code currentLimit} to (current position) + {@code byteLimit}.  This
    531    * is called when descending into a length-delimited embedded message.
    532    *
    533    * @return the old limit.
    534    */
    535   public int pushLimit(int byteLimit) throws InvalidProtocolBufferMicroException {
    536     if (byteLimit < 0) {
    537       throw InvalidProtocolBufferMicroException.negativeSize();
    538     }
    539     byteLimit += totalBytesRetired + bufferPos;
    540     final int oldLimit = currentLimit;
    541     if (byteLimit > oldLimit) {
    542       throw InvalidProtocolBufferMicroException.truncatedMessage();
    543     }
    544     currentLimit = byteLimit;
    545 
    546     recomputeBufferSizeAfterLimit();
    547 
    548     return oldLimit;
    549   }
    550 
    551   private void recomputeBufferSizeAfterLimit() {
    552     bufferSize += bufferSizeAfterLimit;
    553     final int bufferEnd = totalBytesRetired + bufferSize;
    554     if (bufferEnd > currentLimit) {
    555       // Limit is in current buffer.
    556       bufferSizeAfterLimit = bufferEnd - currentLimit;
    557       bufferSize -= bufferSizeAfterLimit;
    558     } else {
    559       bufferSizeAfterLimit = 0;
    560     }
    561   }
    562 
    563   /**
    564    * Discards the current limit, returning to the previous limit.
    565    *
    566    * @param oldLimit The old limit, as returned by {@code pushLimit}.
    567    */
    568   public void popLimit(final int oldLimit) {
    569     currentLimit = oldLimit;
    570     recomputeBufferSizeAfterLimit();
    571   }
    572 
    573   /**
    574    * Returns the number of bytes to be read before the current limit.
    575    * If no limit is set, returns -1.
    576    */
    577   public int getBytesUntilLimit() {
    578     if (currentLimit == Integer.MAX_VALUE) {
    579       return -1;
    580     }
    581 
    582     final int currentAbsolutePosition = totalBytesRetired + bufferPos;
    583     return currentLimit - currentAbsolutePosition;
    584   }
    585 
    586   /**
    587    * Returns true if the stream has reached the end of the input.  This is the
    588    * case if either the end of the underlying input source has been reached or
    589    * if the stream has reached a limit created using {@link #pushLimit(int)}.
    590    */
    591   public boolean isAtEnd() throws IOException {
    592     return bufferPos == bufferSize && !refillBuffer(false);
    593   }
    594 
    595   /**
    596    * Called with {@code this.buffer} is empty to read more bytes from the
    597    * input.  If {@code mustSucceed} is true, refillBuffer() gurantees that
    598    * either there will be at least one byte in the buffer when it returns
    599    * or it will throw an exception.  If {@code mustSucceed} is false,
    600    * refillBuffer() returns false if no more bytes were available.
    601    */
    602   private boolean refillBuffer(final boolean mustSucceed) throws IOException {
    603     if (bufferPos < bufferSize) {
    604       throw new IllegalStateException(
    605         "refillBuffer() called when buffer wasn't empty.");
    606     }
    607 
    608     if (totalBytesRetired + bufferSize == currentLimit) {
    609       // Oops, we hit a limit.
    610       if (mustSucceed) {
    611         throw InvalidProtocolBufferMicroException.truncatedMessage();
    612       } else {
    613         return false;
    614       }
    615     }
    616 
    617     totalBytesRetired += bufferSize;
    618 
    619     bufferPos = 0;
    620     bufferSize = (input == null) ? -1 : input.read(buffer);
    621     if (bufferSize == 0 || bufferSize < -1) {
    622       throw new IllegalStateException(
    623           "InputStream#read(byte[]) returned invalid result: " + bufferSize +
    624           "\nThe InputStream implementation is buggy.");
    625     }
    626     if (bufferSize == -1) {
    627       bufferSize = 0;
    628       if (mustSucceed) {
    629         throw InvalidProtocolBufferMicroException.truncatedMessage();
    630       } else {
    631         return false;
    632       }
    633     } else {
    634       recomputeBufferSizeAfterLimit();
    635       final int totalBytesRead =
    636         totalBytesRetired + bufferSize + bufferSizeAfterLimit;
    637       if (totalBytesRead > sizeLimit || totalBytesRead < 0) {
    638         throw InvalidProtocolBufferMicroException.sizeLimitExceeded();
    639       }
    640       return true;
    641     }
    642   }
    643 
    644   /**
    645    * Read one byte from the input.
    646    *
    647    * @throws InvalidProtocolBufferMicroException The end of the stream or the current
    648    *                                        limit was reached.
    649    */
    650   public byte readRawByte() throws IOException {
    651     if (bufferPos == bufferSize) {
    652       refillBuffer(true);
    653     }
    654     return buffer[bufferPos++];
    655   }
    656 
    657   /**
    658    * Read a fixed size of bytes from the input.
    659    *
    660    * @throws InvalidProtocolBufferMicroException The end of the stream or the current
    661    *                                        limit was reached.
    662    */
    663   public byte[] readRawBytes(final int size) throws IOException {
    664     if (size < 0) {
    665       throw InvalidProtocolBufferMicroException.negativeSize();
    666     }
    667 
    668     if (totalBytesRetired + bufferPos + size > currentLimit) {
    669       // Read to the end of the stream anyway.
    670       skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
    671       // Then fail.
    672       throw InvalidProtocolBufferMicroException.truncatedMessage();
    673     }
    674 
    675     if (size <= bufferSize - bufferPos) {
    676       // We have all the bytes we need already.
    677       final byte[] bytes = new byte[size];
    678       System.arraycopy(buffer, bufferPos, bytes, 0, size);
    679       bufferPos += size;
    680       return bytes;
    681     } else if (size < BUFFER_SIZE) {
    682       // Reading more bytes than are in the buffer, but not an excessive number
    683       // of bytes.  We can safely allocate the resulting array ahead of time.
    684 
    685       // First copy what we have.
    686       final byte[] bytes = new byte[size];
    687       int pos = bufferSize - bufferPos;
    688       System.arraycopy(buffer, bufferPos, bytes, 0, pos);
    689       bufferPos = bufferSize;
    690 
    691       // We want to use refillBuffer() and then copy from the buffer into our
    692       // byte array rather than reading directly into our byte array because
    693       // the input may be unbuffered.
    694       refillBuffer(true);
    695 
    696       while (size - pos > bufferSize) {
    697         System.arraycopy(buffer, 0, bytes, pos, bufferSize);
    698         pos += bufferSize;
    699         bufferPos = bufferSize;
    700         refillBuffer(true);
    701       }
    702 
    703       System.arraycopy(buffer, 0, bytes, pos, size - pos);
    704       bufferPos = size - pos;
    705 
    706       return bytes;
    707     } else {
    708       // The size is very large.  For security reasons, we can't allocate the
    709       // entire byte array yet.  The size comes directly from the input, so a
    710       // maliciously-crafted message could provide a bogus very large size in
    711       // order to trick the app into allocating a lot of memory.  We avoid this
    712       // by allocating and reading only a small chunk at a time, so that the
    713       // malicious message must actually *be* extremely large to cause
    714       // problems.  Meanwhile, we limit the allowed size of a message elsewhere.
    715 
    716       // Remember the buffer markers since we'll have to copy the bytes out of
    717       // it later.
    718       final int originalBufferPos = bufferPos;
    719       final int originalBufferSize = bufferSize;
    720 
    721       // Mark the current buffer consumed.
    722       totalBytesRetired += bufferSize;
    723       bufferPos = 0;
    724       bufferSize = 0;
    725 
    726       // Read all the rest of the bytes we need.
    727       int sizeLeft = size - (originalBufferSize - originalBufferPos);
    728 
    729       // For compatibility with Java 1.3 use Vector
    730       final java.util.Vector chunks = new java.util.Vector();
    731 
    732       while (sizeLeft > 0) {
    733         final byte[] chunk = new byte[Math.min(sizeLeft, BUFFER_SIZE)];
    734         int pos = 0;
    735         while (pos < chunk.length) {
    736           final int n = (input == null) ? -1 :
    737             input.read(chunk, pos, chunk.length - pos);
    738           if (n == -1) {
    739             throw InvalidProtocolBufferMicroException.truncatedMessage();
    740           }
    741           totalBytesRetired += n;
    742           pos += n;
    743         }
    744         sizeLeft -= chunk.length;
    745         chunks.addElement(chunk);
    746       }
    747 
    748       // OK, got everything.  Now concatenate it all into one buffer.
    749       final byte[] bytes = new byte[size];
    750 
    751       // Start by copying the leftover bytes from this.buffer.
    752       int pos = originalBufferSize - originalBufferPos;
    753       System.arraycopy(buffer, originalBufferPos, bytes, 0, pos);
    754 
    755       // And now all the chunks.
    756       for (int i = 0; i < chunks.size(); i++) {
    757         byte [] chunk = (byte [])chunks.elementAt(i);
    758         System.arraycopy(chunk, 0, bytes, pos, chunk.length);
    759         pos += chunk.length;
    760       }
    761 
    762       // Done.
    763       return bytes;
    764     }
    765   }
    766 
    767   /**
    768    * Reads and discards {@code size} bytes.
    769    *
    770    * @throws InvalidProtocolBufferMicroException The end of the stream or the current
    771    *                                        limit was reached.
    772    */
    773   public void skipRawBytes(final int size) throws IOException {
    774     if (size < 0) {
    775       throw InvalidProtocolBufferMicroException.negativeSize();
    776     }
    777 
    778     if (totalBytesRetired + bufferPos + size > currentLimit) {
    779       // Read to the end of the stream anyway.
    780       skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
    781       // Then fail.
    782       throw InvalidProtocolBufferMicroException.truncatedMessage();
    783     }
    784 
    785     if (size <= bufferSize - bufferPos) {
    786       // We have all the bytes we need already.
    787       bufferPos += size;
    788     } else {
    789       // Skipping more bytes than are in the buffer.  First skip what we have.
    790       int pos = bufferSize - bufferPos;
    791       totalBytesRetired += bufferSize;
    792       bufferPos = 0;
    793       bufferSize = 0;
    794 
    795       // Then skip directly from the InputStream for the rest.
    796       while (pos < size) {
    797         final int n = (input == null) ? -1 : (int) input.skip(size - pos);
    798         if (n <= 0) {
    799           throw InvalidProtocolBufferMicroException.truncatedMessage();
    800         }
    801         pos += n;
    802         totalBytesRetired += n;
    803       }
    804     }
    805   }
    806 }
    807