CodedInputStreamMicro.java revision 8a2f7578bb6289415f1d0a01c9cc96d283730480
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.micro;
32
33import java.io.IOException;
34import 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@google.com Kenton Varda
47 */
48public 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 {
247      // Slow path:  Build a byte array first then copy it.
248      return ByteStringMicro.copyFrom(readRawBytes(size));
249    }
250  }
251
252  /** Read a {@code uint32} field value from the stream. */
253  public int readUInt32() throws IOException {
254    return readRawVarint32();
255  }
256
257  /**
258   * Read an enum field value from the stream.  Caller is responsible
259   * for converting the numeric value to an actual enum.
260   */
261  public int readEnum() throws IOException {
262    return readRawVarint32();
263  }
264
265  /** Read an {@code sfixed32} field value from the stream. */
266  public int readSFixed32() throws IOException {
267    return readRawLittleEndian32();
268  }
269
270  /** Read an {@code sfixed64} field value from the stream. */
271  public long readSFixed64() throws IOException {
272    return readRawLittleEndian64();
273  }
274
275  /** Read an {@code sint32} field value from the stream. */
276  public int readSInt32() throws IOException {
277    return decodeZigZag32(readRawVarint32());
278  }
279
280  /** Read an {@code sint64} field value from the stream. */
281  public long readSInt64() throws IOException {
282    return decodeZigZag64(readRawVarint64());
283  }
284
285  // =================================================================
286
287  /**
288   * Read a raw Varint from the stream.  If larger than 32 bits, discard the
289   * upper bits.
290   */
291  public int readRawVarint32() throws IOException {
292    byte tmp = readRawByte();
293    if (tmp >= 0) {
294      return tmp;
295    }
296    int result = tmp & 0x7f;
297    if ((tmp = readRawByte()) >= 0) {
298      result |= tmp << 7;
299    } else {
300      result |= (tmp & 0x7f) << 7;
301      if ((tmp = readRawByte()) >= 0) {
302        result |= tmp << 14;
303      } else {
304        result |= (tmp & 0x7f) << 14;
305        if ((tmp = readRawByte()) >= 0) {
306          result |= tmp << 21;
307        } else {
308          result |= (tmp & 0x7f) << 21;
309          result |= (tmp = readRawByte()) << 28;
310          if (tmp < 0) {
311            // Discard upper 32 bits.
312            for (int i = 0; i < 5; i++) {
313              if (readRawByte() >= 0) {
314                return result;
315              }
316            }
317            throw InvalidProtocolBufferMicroException.malformedVarint();
318          }
319        }
320      }
321    }
322    return result;
323  }
324
325  /**
326   * Reads a varint from the input one byte at a time, so that it does not
327   * read any bytes after the end of the varint.  If you simply wrapped the
328   * stream in a CodedInputStream and used {@link #readRawVarint32(InputStream)}
329   * then you would probably end up reading past the end of the varint since
330   * CodedInputStream buffers its input.
331   */
332  static int readRawVarint32(final InputStream input) throws IOException {
333    int result = 0;
334    int offset = 0;
335    for (; offset < 32; offset += 7) {
336      final int b = input.read();
337      if (b == -1) {
338        throw InvalidProtocolBufferMicroException.truncatedMessage();
339      }
340      result |= (b & 0x7f) << offset;
341      if ((b & 0x80) == 0) {
342        return result;
343      }
344    }
345    // Keep reading up to 64 bits.
346    for (; offset < 64; offset += 7) {
347      final int b = input.read();
348      if (b == -1) {
349        throw InvalidProtocolBufferMicroException.truncatedMessage();
350      }
351      if ((b & 0x80) == 0) {
352        return result;
353      }
354    }
355    throw InvalidProtocolBufferMicroException.malformedVarint();
356  }
357
358  /** Read a raw Varint from the stream. */
359  public long readRawVarint64() throws IOException {
360    int shift = 0;
361    long result = 0;
362    while (shift < 64) {
363      final byte b = readRawByte();
364      result |= (long)(b & 0x7F) << shift;
365      if ((b & 0x80) == 0) {
366        return result;
367      }
368      shift += 7;
369    }
370    throw InvalidProtocolBufferMicroException.malformedVarint();
371  }
372
373  /** Read a 32-bit little-endian integer from the stream. */
374  public int readRawLittleEndian32() throws IOException {
375    final byte b1 = readRawByte();
376    final byte b2 = readRawByte();
377    final byte b3 = readRawByte();
378    final byte b4 = readRawByte();
379    return ((b1 & 0xff)      ) |
380           ((b2 & 0xff) <<  8) |
381           ((b3 & 0xff) << 16) |
382           ((b4 & 0xff) << 24);
383  }
384
385  /** Read a 64-bit little-endian integer from the stream. */
386  public long readRawLittleEndian64() throws IOException {
387    final byte b1 = readRawByte();
388    final byte b2 = readRawByte();
389    final byte b3 = readRawByte();
390    final byte b4 = readRawByte();
391    final byte b5 = readRawByte();
392    final byte b6 = readRawByte();
393    final byte b7 = readRawByte();
394    final byte b8 = readRawByte();
395    return (((long)b1 & 0xff)      ) |
396           (((long)b2 & 0xff) <<  8) |
397           (((long)b3 & 0xff) << 16) |
398           (((long)b4 & 0xff) << 24) |
399           (((long)b5 & 0xff) << 32) |
400           (((long)b6 & 0xff) << 40) |
401           (((long)b7 & 0xff) << 48) |
402           (((long)b8 & 0xff) << 56);
403  }
404
405  /**
406   * Decode a ZigZag-encoded 32-bit value.  ZigZag encodes signed integers
407   * into values that can be efficiently encoded with varint.  (Otherwise,
408   * negative values must be sign-extended to 64 bits to be varint encoded,
409   * thus always taking 10 bytes on the wire.)
410   *
411   * @param n An unsigned 32-bit integer, stored in a signed int because
412   *          Java has no explicit unsigned support.
413   * @return A signed 32-bit integer.
414   */
415  public static int decodeZigZag32(final int n) {
416    return (n >>> 1) ^ -(n & 1);
417  }
418
419  /**
420   * Decode a ZigZag-encoded 64-bit value.  ZigZag encodes signed integers
421   * into values that can be efficiently encoded with varint.  (Otherwise,
422   * negative values must be sign-extended to 64 bits to be varint encoded,
423   * thus always taking 10 bytes on the wire.)
424   *
425   * @param n An unsigned 64-bit integer, stored in a signed int because
426   *          Java has no explicit unsigned support.
427   * @return A signed 64-bit integer.
428   */
429  public static long decodeZigZag64(final long n) {
430    return (n >>> 1) ^ -(n & 1);
431  }
432
433  // -----------------------------------------------------------------
434
435  private final byte[] buffer;
436  private int bufferSize;
437  private int bufferSizeAfterLimit;
438  private int bufferPos;
439  private final InputStream input;
440  private int lastTag;
441
442  /**
443   * The total number of bytes read before the current buffer.  The total
444   * bytes read up to the current position can be computed as
445   * {@code totalBytesRetired + bufferPos}.
446   */
447  private int totalBytesRetired;
448
449  /** The absolute position of the end of the current message. */
450  private int currentLimit = Integer.MAX_VALUE;
451
452  /** See setRecursionLimit() */
453  private int recursionDepth;
454  private int recursionLimit = DEFAULT_RECURSION_LIMIT;
455
456  /** See setSizeLimit() */
457  private int sizeLimit = DEFAULT_SIZE_LIMIT;
458
459  private static final int DEFAULT_RECURSION_LIMIT = 64;
460  private static final int DEFAULT_SIZE_LIMIT = 64 << 20;  // 64MB
461  private static final int BUFFER_SIZE = 4096;
462
463  private CodedInputStreamMicro(final byte[] buffer, final int off, final int len) {
464    this.buffer = buffer;
465    bufferSize = off + len;
466    bufferPos = off;
467    input = null;
468  }
469
470  private CodedInputStreamMicro(final InputStream input) {
471    buffer = new byte[BUFFER_SIZE];
472    bufferSize = 0;
473    bufferPos = 0;
474    this.input = input;
475  }
476
477  /**
478   * Set the maximum message recursion depth.  In order to prevent malicious
479   * messages from causing stack overflows, {@code CodedInputStream} limits
480   * how deeply messages may be nested.  The default limit is 64.
481   *
482   * @return the old limit.
483   */
484  public int setRecursionLimit(final int limit) {
485    if (limit < 0) {
486      throw new IllegalArgumentException(
487        "Recursion limit cannot be negative: " + limit);
488    }
489    final int oldLimit = recursionLimit;
490    recursionLimit = limit;
491    return oldLimit;
492  }
493
494  /**
495   * Set the maximum message size.  In order to prevent malicious
496   * messages from exhausting memory or causing integer overflows,
497   * {@code CodedInputStream} limits how large a message may be.
498   * The default limit is 64MB.  You should set this limit as small
499   * as you can without harming your app's functionality.  Note that
500   * size limits only apply when reading from an {@code InputStream}, not
501   * when constructed around a raw byte array (nor with
502   * {@link ByteStringMicro#newCodedInput}).
503   * <p>
504   * If you want to read several messages from a single CodedInputStream, you
505   * could call {@link #resetSizeCounter()} after each one to avoid hitting the
506   * size limit.
507   *
508   * @return the old limit.
509   */
510  public int setSizeLimit(final int limit) {
511    if (limit < 0) {
512      throw new IllegalArgumentException(
513        "Size limit cannot be negative: " + limit);
514    }
515    final int oldLimit = sizeLimit;
516    sizeLimit = limit;
517    return oldLimit;
518  }
519
520  /**
521   * Resets the current size counter to zero (see {@link #setSizeLimit(int)}).
522   */
523  public void resetSizeCounter() {
524    totalBytesRetired = 0;
525  }
526
527  /**
528   * Sets {@code currentLimit} to (current position) + {@code byteLimit}.  This
529   * is called when descending into a length-delimited embedded message.
530   *
531   * @return the old limit.
532   */
533  public int pushLimit(int byteLimit) throws InvalidProtocolBufferMicroException {
534    if (byteLimit < 0) {
535      throw InvalidProtocolBufferMicroException.negativeSize();
536    }
537    byteLimit += totalBytesRetired + bufferPos;
538    final int oldLimit = currentLimit;
539    if (byteLimit > oldLimit) {
540      throw InvalidProtocolBufferMicroException.truncatedMessage();
541    }
542    currentLimit = byteLimit;
543
544    recomputeBufferSizeAfterLimit();
545
546    return oldLimit;
547  }
548
549  private void recomputeBufferSizeAfterLimit() {
550    bufferSize += bufferSizeAfterLimit;
551    final int bufferEnd = totalBytesRetired + bufferSize;
552    if (bufferEnd > currentLimit) {
553      // Limit is in current buffer.
554      bufferSizeAfterLimit = bufferEnd - currentLimit;
555      bufferSize -= bufferSizeAfterLimit;
556    } else {
557      bufferSizeAfterLimit = 0;
558    }
559  }
560
561  /**
562   * Discards the current limit, returning to the previous limit.
563   *
564   * @param oldLimit The old limit, as returned by {@code pushLimit}.
565   */
566  public void popLimit(final int oldLimit) {
567    currentLimit = oldLimit;
568    recomputeBufferSizeAfterLimit();
569  }
570
571  /**
572   * Returns the number of bytes to be read before the current limit.
573   * If no limit is set, returns -1.
574   */
575  public int getBytesUntilLimit() {
576    if (currentLimit == Integer.MAX_VALUE) {
577      return -1;
578    }
579
580    final int currentAbsolutePosition = totalBytesRetired + bufferPos;
581    return currentLimit - currentAbsolutePosition;
582  }
583
584  /**
585   * Returns true if the stream has reached the end of the input.  This is the
586   * case if either the end of the underlying input source has been reached or
587   * if the stream has reached a limit created using {@link #pushLimit(int)}.
588   */
589  public boolean isAtEnd() throws IOException {
590    return bufferPos == bufferSize && !refillBuffer(false);
591  }
592
593  /**
594   * Called with {@code this.buffer} is empty to read more bytes from the
595   * input.  If {@code mustSucceed} is true, refillBuffer() gurantees that
596   * either there will be at least one byte in the buffer when it returns
597   * or it will throw an exception.  If {@code mustSucceed} is false,
598   * refillBuffer() returns false if no more bytes were available.
599   */
600  private boolean refillBuffer(final boolean mustSucceed) throws IOException {
601    if (bufferPos < bufferSize) {
602      throw new IllegalStateException(
603        "refillBuffer() called when buffer wasn't empty.");
604    }
605
606    if (totalBytesRetired + bufferSize == currentLimit) {
607      // Oops, we hit a limit.
608      if (mustSucceed) {
609        throw InvalidProtocolBufferMicroException.truncatedMessage();
610      } else {
611        return false;
612      }
613    }
614
615    totalBytesRetired += bufferSize;
616
617    bufferPos = 0;
618    bufferSize = (input == null) ? -1 : input.read(buffer);
619    if (bufferSize == 0 || bufferSize < -1) {
620      throw new IllegalStateException(
621          "InputStream#read(byte[]) returned invalid result: " + bufferSize +
622          "\nThe InputStream implementation is buggy.");
623    }
624    if (bufferSize == -1) {
625      bufferSize = 0;
626      if (mustSucceed) {
627        throw InvalidProtocolBufferMicroException.truncatedMessage();
628      } else {
629        return false;
630      }
631    } else {
632      recomputeBufferSizeAfterLimit();
633      final int totalBytesRead =
634        totalBytesRetired + bufferSize + bufferSizeAfterLimit;
635      if (totalBytesRead > sizeLimit || totalBytesRead < 0) {
636        throw InvalidProtocolBufferMicroException.sizeLimitExceeded();
637      }
638      return true;
639    }
640  }
641
642  /**
643   * Read one byte from the input.
644   *
645   * @throws InvalidProtocolBufferMicroException The end of the stream or the current
646   *                                        limit was reached.
647   */
648  public byte readRawByte() throws IOException {
649    if (bufferPos == bufferSize) {
650      refillBuffer(true);
651    }
652    return buffer[bufferPos++];
653  }
654
655  /**
656   * Read a fixed size of bytes from the input.
657   *
658   * @throws InvalidProtocolBufferMicroException The end of the stream or the current
659   *                                        limit was reached.
660   */
661  public byte[] readRawBytes(final int size) throws IOException {
662    if (size < 0) {
663      throw InvalidProtocolBufferMicroException.negativeSize();
664    }
665
666    if (totalBytesRetired + bufferPos + size > currentLimit) {
667      // Read to the end of the stream anyway.
668      skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
669      // Then fail.
670      throw InvalidProtocolBufferMicroException.truncatedMessage();
671    }
672
673    if (size <= bufferSize - bufferPos) {
674      // We have all the bytes we need already.
675      final byte[] bytes = new byte[size];
676      System.arraycopy(buffer, bufferPos, bytes, 0, size);
677      bufferPos += size;
678      return bytes;
679    } else if (size < BUFFER_SIZE) {
680      // Reading more bytes than are in the buffer, but not an excessive number
681      // of bytes.  We can safely allocate the resulting array ahead of time.
682
683      // First copy what we have.
684      final byte[] bytes = new byte[size];
685      int pos = bufferSize - bufferPos;
686      System.arraycopy(buffer, bufferPos, bytes, 0, pos);
687      bufferPos = bufferSize;
688
689      // We want to use refillBuffer() and then copy from the buffer into our
690      // byte array rather than reading directly into our byte array because
691      // the input may be unbuffered.
692      refillBuffer(true);
693
694      while (size - pos > bufferSize) {
695        System.arraycopy(buffer, 0, bytes, pos, bufferSize);
696        pos += bufferSize;
697        bufferPos = bufferSize;
698        refillBuffer(true);
699      }
700
701      System.arraycopy(buffer, 0, bytes, pos, size - pos);
702      bufferPos = size - pos;
703
704      return bytes;
705    } else {
706      // The size is very large.  For security reasons, we can't allocate the
707      // entire byte array yet.  The size comes directly from the input, so a
708      // maliciously-crafted message could provide a bogus very large size in
709      // order to trick the app into allocating a lot of memory.  We avoid this
710      // by allocating and reading only a small chunk at a time, so that the
711      // malicious message must actually *be* extremely large to cause
712      // problems.  Meanwhile, we limit the allowed size of a message elsewhere.
713
714      // Remember the buffer markers since we'll have to copy the bytes out of
715      // it later.
716      final int originalBufferPos = bufferPos;
717      final int originalBufferSize = bufferSize;
718
719      // Mark the current buffer consumed.
720      totalBytesRetired += bufferSize;
721      bufferPos = 0;
722      bufferSize = 0;
723
724      // Read all the rest of the bytes we need.
725      int sizeLeft = size - (originalBufferSize - originalBufferPos);
726
727      // For compatibility with Java 1.3 use Vector
728      final java.util.Vector chunks = new java.util.Vector();
729
730      while (sizeLeft > 0) {
731        final byte[] chunk = new byte[Math.min(sizeLeft, BUFFER_SIZE)];
732        int pos = 0;
733        while (pos < chunk.length) {
734          final int n = (input == null) ? -1 :
735            input.read(chunk, pos, chunk.length - pos);
736          if (n == -1) {
737            throw InvalidProtocolBufferMicroException.truncatedMessage();
738          }
739          totalBytesRetired += n;
740          pos += n;
741        }
742        sizeLeft -= chunk.length;
743        chunks.addElement(chunk);
744      }
745
746      // OK, got everything.  Now concatenate it all into one buffer.
747      final byte[] bytes = new byte[size];
748
749      // Start by copying the leftover bytes from this.buffer.
750      int pos = originalBufferSize - originalBufferPos;
751      System.arraycopy(buffer, originalBufferPos, bytes, 0, pos);
752
753      // And now all the chunks.
754      for (int i = 0; i < chunks.size(); i++) {
755        byte [] chunk = (byte [])chunks.elementAt(i);
756        System.arraycopy(chunk, 0, bytes, pos, chunk.length);
757        pos += chunk.length;
758      }
759
760      // Done.
761      return bytes;
762    }
763  }
764
765  /**
766   * Reads and discards {@code size} bytes.
767   *
768   * @throws InvalidProtocolBufferMicroException The end of the stream or the current
769   *                                        limit was reached.
770   */
771  public void skipRawBytes(final int size) throws IOException {
772    if (size < 0) {
773      throw InvalidProtocolBufferMicroException.negativeSize();
774    }
775
776    if (totalBytesRetired + bufferPos + size > currentLimit) {
777      // Read to the end of the stream anyway.
778      skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
779      // Then fail.
780      throw InvalidProtocolBufferMicroException.truncatedMessage();
781    }
782
783    if (size <= bufferSize - bufferPos) {
784      // We have all the bytes we need already.
785      bufferPos += size;
786    } else {
787      // Skipping more bytes than are in the buffer.  First skip what we have.
788      int pos = bufferSize - bufferPos;
789      totalBytesRetired += bufferSize;
790      bufferPos = 0;
791      bufferSize = 0;
792
793      // Then skip directly from the InputStream for the rest.
794      while (pos < size) {
795        final int n = (input == null) ? -1 : (int) input.skip(size - pos);
796        if (n <= 0) {
797          throw InvalidProtocolBufferMicroException.truncatedMessage();
798        }
799        pos += n;
800        totalBytesRetired += n;
801      }
802    }
803  }
804}
805