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 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