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