1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // http://code.google.com/p/protobuf/
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
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
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.
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.
31 package com.google.protobuf;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.util.ArrayList;
36 import java.util.List;
39 * Reads and decodes protocol message fields.
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.
48 * @author kenton@google.com Kenton Varda
50 public final class CodedInputStream {
52 * Create a new CodedInputStream wrapping the given InputStream.
54 public static CodedInputStream newInstance(final InputStream input) {
55 return new CodedInputStream(input);
59 * Create a new CodedInputStream wrapping the given byte array.
61 public static CodedInputStream newInstance(final byte[] buf) {
62 return newInstance(buf, 0, buf.length);
66 * Create a new CodedInputStream wrapping the given byte array slice.
68 public static CodedInputStream newInstance(final byte[] buf, final int off,
70 CodedInputStream result = new CodedInputStream(buf, off, len);
72 // Some uses of CodedInputStream can be more efficient if they know
73 // exactly how many bytes are available. By pushing the end point of the
74 // buffer as a limit, we allow them to get this information via
75 // getBytesUntilLimit(). Pushing a limit that we know is at the end of
76 // the stream can never hurt, since we can never past that point anyway.
77 result.pushLimit(len);
78 } catch (InvalidProtocolBufferException ex) {
79 // The only reason pushLimit() might throw an exception here is if len
80 // is negative. Normally pushLimit()'s parameter comes directly off the
81 // wire, so it's important to catch exceptions in case of corrupt or
82 // malicious data. However, in this case, we expect that len is not a
83 // user-supplied value, so we can assume that it being negative indicates
84 // a programming error. Therefore, throwing an unchecked exception is
86 throw new IllegalArgumentException(ex);
91 // -----------------------------------------------------------------
94 * Attempt to read a field tag, returning zero if we have reached EOF.
95 * Protocol message parsers use this to read tags, since a protocol message
96 * may legally end wherever a tag occurs, and zero is not a valid tag number.
98 public int readTag() throws IOException {
104 lastTag = readRawVarint32();
105 if (WireFormat.getTagFieldNumber(lastTag) == 0) {
106 // If we actually read zero (or any tag number corresponding to field
107 // number zero), that's not a valid tag.
108 throw InvalidProtocolBufferException.invalidTag();
114 * Verifies that the last call to readTag() returned the given tag value.
115 * This is used to verify that a nested group ended with the correct
118 * @throws InvalidProtocolBufferException {@code value} does not match the
121 public void checkLastTagWas(final int value)
122 throws InvalidProtocolBufferException {
123 if (lastTag != value) {
124 throw InvalidProtocolBufferException.invalidEndTag();
129 * Reads and discards a single field, given its tag value.
131 * @return {@code false} if the tag is an endgroup tag, in which case
132 * nothing is skipped. Otherwise, returns {@code true}.
134 public boolean skipField(final int tag) throws IOException {
135 switch (WireFormat.getTagWireType(tag)) {
136 case WireFormat.WIRETYPE_VARINT:
139 case WireFormat.WIRETYPE_FIXED64:
140 readRawLittleEndian64();
142 case WireFormat.WIRETYPE_LENGTH_DELIMITED:
143 skipRawBytes(readRawVarint32());
145 case WireFormat.WIRETYPE_START_GROUP:
148 WireFormat.makeTag(WireFormat.getTagFieldNumber(tag),
149 WireFormat.WIRETYPE_END_GROUP));
151 case WireFormat.WIRETYPE_END_GROUP:
153 case WireFormat.WIRETYPE_FIXED32:
154 readRawLittleEndian32();
157 throw InvalidProtocolBufferException.invalidWireType();
162 * Reads and discards an entire message. This will read either until EOF
163 * or until an endgroup tag, whichever comes first.
165 public void skipMessage() throws IOException {
167 final int tag = readTag();
168 if (tag == 0 || !skipField(tag)) {
174 // -----------------------------------------------------------------
176 /** Read a {@code double} field value from the stream. */
177 public double readDouble() throws IOException {
178 return Double.longBitsToDouble(readRawLittleEndian64());
181 /** Read a {@code float} field value from the stream. */
182 public float readFloat() throws IOException {
183 return Float.intBitsToFloat(readRawLittleEndian32());
186 /** Read a {@code uint64} field value from the stream. */
187 public long readUInt64() throws IOException {
188 return readRawVarint64();
191 /** Read an {@code int64} field value from the stream. */
192 public long readInt64() throws IOException {
193 return readRawVarint64();
196 /** Read an {@code int32} field value from the stream. */
197 public int readInt32() throws IOException {
198 return readRawVarint32();
201 /** Read a {@code fixed64} field value from the stream. */
202 public long readFixed64() throws IOException {
203 return readRawLittleEndian64();
206 /** Read a {@code fixed32} field value from the stream. */
207 public int readFixed32() throws IOException {
208 return readRawLittleEndian32();
211 /** Read a {@code bool} field value from the stream. */
212 public boolean readBool() throws IOException {
213 return readRawVarint32() != 0;
216 /** Read a {@code string} field value from the stream. */
217 public String readString() throws IOException {
218 final int size = readRawVarint32();
219 if (size <= (bufferSize - bufferPos) && size > 0) {
220 // Fast path: We already have the bytes in a contiguous buffer, so
221 // just copy directly from it.
222 final String result = new String(buffer, bufferPos, size, "UTF-8");
226 // Slow path: Build a byte array first then copy it.
227 return new String(readRawBytes(size), "UTF-8");
231 /** Read a {@code group} field value from the stream. */
232 public void readGroup(final int fieldNumber,
233 final MessageLite.Builder builder,
234 final ExtensionRegistryLite extensionRegistry)
236 if (recursionDepth >= recursionLimit) {
237 throw InvalidProtocolBufferException.recursionLimitExceeded();
240 builder.mergeFrom(this, extensionRegistry);
242 WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
246 /** Read a {@code group} field value from the stream. */
247 public <T extends MessageLite> T readGroup(
248 final int fieldNumber,
249 final Parser<T> parser,
250 final ExtensionRegistryLite extensionRegistry)
252 if (recursionDepth >= recursionLimit) {
253 throw InvalidProtocolBufferException.recursionLimitExceeded();
256 T result = parser.parsePartialFrom(this, extensionRegistry);
258 WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
264 * Reads a {@code group} field value from the stream and merges it into the
265 * given {@link UnknownFieldSet}.
267 * @deprecated UnknownFieldSet.Builder now implements MessageLite.Builder, so
268 * you can just call {@link #readGroup}.
271 public void readUnknownGroup(final int fieldNumber,
272 final MessageLite.Builder builder)
274 // We know that UnknownFieldSet will ignore any ExtensionRegistry so it
275 // is safe to pass null here. (We can't call
276 // ExtensionRegistry.getEmptyRegistry() because that would make this
277 // class depend on ExtensionRegistry, which is not part of the lite
279 readGroup(fieldNumber, builder, null);
282 /** Read an embedded message field value from the stream. */
283 public void readMessage(final MessageLite.Builder builder,
284 final ExtensionRegistryLite extensionRegistry)
286 final int length = readRawVarint32();
287 if (recursionDepth >= recursionLimit) {
288 throw InvalidProtocolBufferException.recursionLimitExceeded();
290 final int oldLimit = pushLimit(length);
292 builder.mergeFrom(this, extensionRegistry);
298 /** Read an embedded message field value from the stream. */
299 public <T extends MessageLite> T readMessage(
300 final Parser<T> parser,
301 final ExtensionRegistryLite extensionRegistry)
303 int length = readRawVarint32();
304 if (recursionDepth >= recursionLimit) {
305 throw InvalidProtocolBufferException.recursionLimitExceeded();
307 final int oldLimit = pushLimit(length);
309 T result = parser.parsePartialFrom(this, extensionRegistry);
316 /** Read a {@code bytes} field value from the stream. */
317 public ByteString readBytes() throws IOException {
318 final int size = readRawVarint32();
320 return ByteString.EMPTY;
321 } else if (size <= (bufferSize - bufferPos) && size > 0) {
322 // Fast path: We already have the bytes in a contiguous buffer, so
323 // just copy directly from it.
324 final ByteString result = ByteString.copyFrom(buffer, bufferPos, size);
328 // Slow path: Build a byte array first then copy it.
329 return ByteString.copyFrom(readRawBytes(size));
333 /** Read a {@code uint32} field value from the stream. */
334 public int readUInt32() throws IOException {
335 return readRawVarint32();
339 * Read an enum field value from the stream. Caller is responsible
340 * for converting the numeric value to an actual enum.
342 public int readEnum() throws IOException {
343 return readRawVarint32();
346 /** Read an {@code sfixed32} field value from the stream. */
347 public int readSFixed32() throws IOException {
348 return readRawLittleEndian32();
351 /** Read an {@code sfixed64} field value from the stream. */
352 public long readSFixed64() throws IOException {
353 return readRawLittleEndian64();
356 /** Read an {@code sint32} field value from the stream. */
357 public int readSInt32() throws IOException {
358 return decodeZigZag32(readRawVarint32());
361 /** Read an {@code sint64} field value from the stream. */
362 public long readSInt64() throws IOException {
363 return decodeZigZag64(readRawVarint64());
366 // =================================================================
369 * Read a raw Varint from the stream. If larger than 32 bits, discard the
372 public int readRawVarint32() throws IOException {
373 byte tmp = readRawByte();
377 int result = tmp & 0x7f;
378 if ((tmp = readRawByte()) >= 0) {
381 result |= (tmp & 0x7f) << 7;
382 if ((tmp = readRawByte()) >= 0) {
385 result |= (tmp & 0x7f) << 14;
386 if ((tmp = readRawByte()) >= 0) {
389 result |= (tmp & 0x7f) << 21;
390 result |= (tmp = readRawByte()) << 28;
392 // Discard upper 32 bits.
393 for (int i = 0; i < 5; i++) {
394 if (readRawByte() >= 0) {
398 throw InvalidProtocolBufferException.malformedVarint();
407 * Reads a varint from the input one byte at a time, so that it does not
408 * read any bytes after the end of the varint. If you simply wrapped the
409 * stream in a CodedInputStream and used {@link #readRawVarint32(InputStream)}
410 * then you would probably end up reading past the end of the varint since
411 * CodedInputStream buffers its input.
413 static int readRawVarint32(final InputStream input) throws IOException {
414 final int firstByte = input.read();
415 if (firstByte == -1) {
416 throw InvalidProtocolBufferException.truncatedMessage();
418 return readRawVarint32(firstByte, input);
422 * Like {@link #readRawVarint32(InputStream)}, but expects that the caller
423 * has already read one byte. This allows the caller to determine if EOF
424 * has been reached before attempting to read.
426 public static int readRawVarint32(
427 final int firstByte, final InputStream input) throws IOException {
428 if ((firstByte & 0x80) == 0) {
432 int result = firstByte & 0x7f;
434 for (; offset < 32; offset += 7) {
435 final int b = input.read();
437 throw InvalidProtocolBufferException.truncatedMessage();
439 result |= (b & 0x7f) << offset;
440 if ((b & 0x80) == 0) {
444 // Keep reading up to 64 bits.
445 for (; offset < 64; offset += 7) {
446 final int b = input.read();
448 throw InvalidProtocolBufferException.truncatedMessage();
450 if ((b & 0x80) == 0) {
454 throw InvalidProtocolBufferException.malformedVarint();
457 /** Read a raw Varint from the stream. */
458 public long readRawVarint64() throws IOException {
462 final byte b = readRawByte();
463 result |= (long)(b & 0x7F) << shift;
464 if ((b & 0x80) == 0) {
469 throw InvalidProtocolBufferException.malformedVarint();
472 /** Read a 32-bit little-endian integer from the stream. */
473 public int readRawLittleEndian32() throws IOException {
474 final byte b1 = readRawByte();
475 final byte b2 = readRawByte();
476 final byte b3 = readRawByte();
477 final byte b4 = readRawByte();
478 return (((int)b1 & 0xff) ) |
479 (((int)b2 & 0xff) << 8) |
480 (((int)b3 & 0xff) << 16) |
481 (((int)b4 & 0xff) << 24);
484 /** Read a 64-bit little-endian integer from the stream. */
485 public long readRawLittleEndian64() throws IOException {
486 final byte b1 = readRawByte();
487 final byte b2 = readRawByte();
488 final byte b3 = readRawByte();
489 final byte b4 = readRawByte();
490 final byte b5 = readRawByte();
491 final byte b6 = readRawByte();
492 final byte b7 = readRawByte();
493 final byte b8 = readRawByte();
494 return (((long)b1 & 0xff) ) |
495 (((long)b2 & 0xff) << 8) |
496 (((long)b3 & 0xff) << 16) |
497 (((long)b4 & 0xff) << 24) |
498 (((long)b5 & 0xff) << 32) |
499 (((long)b6 & 0xff) << 40) |
500 (((long)b7 & 0xff) << 48) |
501 (((long)b8 & 0xff) << 56);
505 * Decode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers
506 * into values that can be efficiently encoded with varint. (Otherwise,
507 * negative values must be sign-extended to 64 bits to be varint encoded,
508 * thus always taking 10 bytes on the wire.)
510 * @param n An unsigned 32-bit integer, stored in a signed int because
511 * Java has no explicit unsigned support.
512 * @return A signed 32-bit integer.
514 public static int decodeZigZag32(final int n) {
515 return (n >>> 1) ^ -(n & 1);
519 * Decode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers
520 * into values that can be efficiently encoded with varint. (Otherwise,
521 * negative values must be sign-extended to 64 bits to be varint encoded,
522 * thus always taking 10 bytes on the wire.)
524 * @param n An unsigned 64-bit integer, stored in a signed int because
525 * Java has no explicit unsigned support.
526 * @return A signed 64-bit integer.
528 public static long decodeZigZag64(final long n) {
529 return (n >>> 1) ^ -(n & 1);
532 // -----------------------------------------------------------------
534 private final byte[] buffer;
535 private int bufferSize;
536 private int bufferSizeAfterLimit;
537 private int bufferPos;
538 private final InputStream input;
542 * The total number of bytes read before the current buffer. The total
543 * bytes read up to the current position can be computed as
544 * {@code totalBytesRetired + bufferPos}. This value may be negative if
545 * reading started in the middle of the current buffer (e.g. if the
546 * constructor that takes a byte array and an offset was used).
548 private int totalBytesRetired;
550 /** The absolute position of the end of the current message. */
551 private int currentLimit = Integer.MAX_VALUE;
553 /** See setRecursionLimit() */
554 private int recursionDepth;
555 private int recursionLimit = DEFAULT_RECURSION_LIMIT;
557 /** See setSizeLimit() */
558 private int sizeLimit = DEFAULT_SIZE_LIMIT;
560 private static final int DEFAULT_RECURSION_LIMIT = 64;
561 private static final int DEFAULT_SIZE_LIMIT = 64 << 20; // 64MB
562 private static final int BUFFER_SIZE = 4096;
564 private CodedInputStream(final byte[] buffer, final int off, final int len) {
565 this.buffer = buffer;
566 bufferSize = off + len;
568 totalBytesRetired = -off;
572 private CodedInputStream(final InputStream input) {
573 buffer = new byte[BUFFER_SIZE];
576 totalBytesRetired = 0;
581 * Set the maximum message recursion depth. In order to prevent malicious
582 * messages from causing stack overflows, {@code CodedInputStream} limits
583 * how deeply messages may be nested. The default limit is 64.
585 * @return the old limit.
587 public int setRecursionLimit(final int limit) {
589 throw new IllegalArgumentException(
590 "Recursion limit cannot be negative: " + limit);
592 final int oldLimit = recursionLimit;
593 recursionLimit = limit;
598 * Set the maximum message size. In order to prevent malicious
599 * messages from exhausting memory or causing integer overflows,
600 * {@code CodedInputStream} limits how large a message may be.
601 * The default limit is 64MB. You should set this limit as small
602 * as you can without harming your app's functionality. Note that
603 * size limits only apply when reading from an {@code InputStream}, not
604 * when constructed around a raw byte array (nor with
605 * {@link ByteString#newCodedInput}).
607 * If you want to read several messages from a single CodedInputStream, you
608 * could call {@link #resetSizeCounter()} after each one to avoid hitting the
611 * @return the old limit.
613 public int setSizeLimit(final int limit) {
615 throw new IllegalArgumentException(
616 "Size limit cannot be negative: " + limit);
618 final int oldLimit = sizeLimit;
624 * Resets the current size counter to zero (see {@link #setSizeLimit(int)}).
626 public void resetSizeCounter() {
627 totalBytesRetired = -bufferPos;
631 * Sets {@code currentLimit} to (current position) + {@code byteLimit}. This
632 * is called when descending into a length-delimited embedded message.
634 * <p>Note that {@code pushLimit()} does NOT affect how many bytes the
635 * {@code CodedInputStream} reads from an underlying {@code InputStream} when
636 * refreshing its buffer. If you need to prevent reading past a certain
637 * point in the underlying {@code InputStream} (e.g. because you expect it to
638 * contain more data after the end of the message which you need to handle
639 * differently) then you must place a wrapper around your {@code InputStream}
640 * which limits the amount of data that can be read from it.
642 * @return the old limit.
644 public int pushLimit(int byteLimit) throws InvalidProtocolBufferException {
646 throw InvalidProtocolBufferException.negativeSize();
648 byteLimit += totalBytesRetired + bufferPos;
649 final int oldLimit = currentLimit;
650 if (byteLimit > oldLimit) {
651 throw InvalidProtocolBufferException.truncatedMessage();
653 currentLimit = byteLimit;
655 recomputeBufferSizeAfterLimit();
660 private void recomputeBufferSizeAfterLimit() {
661 bufferSize += bufferSizeAfterLimit;
662 final int bufferEnd = totalBytesRetired + bufferSize;
663 if (bufferEnd > currentLimit) {
664 // Limit is in current buffer.
665 bufferSizeAfterLimit = bufferEnd - currentLimit;
666 bufferSize -= bufferSizeAfterLimit;
668 bufferSizeAfterLimit = 0;
673 * Discards the current limit, returning to the previous limit.
675 * @param oldLimit The old limit, as returned by {@code pushLimit}.
677 public void popLimit(final int oldLimit) {
678 currentLimit = oldLimit;
679 recomputeBufferSizeAfterLimit();
683 * Returns the number of bytes to be read before the current limit.
684 * If no limit is set, returns -1.
686 public int getBytesUntilLimit() {
687 if (currentLimit == Integer.MAX_VALUE) {
691 final int currentAbsolutePosition = totalBytesRetired + bufferPos;
692 return currentLimit - currentAbsolutePosition;
696 * Returns true if the stream has reached the end of the input. This is the
697 * case if either the end of the underlying input source has been reached or
698 * if the stream has reached a limit created using {@link #pushLimit(int)}.
700 public boolean isAtEnd() throws IOException {
701 return bufferPos == bufferSize && !refillBuffer(false);
705 * The total bytes read up to the current position. Calling
706 * {@link #resetSizeCounter()} resets this value to zero.
708 public int getTotalBytesRead() {
709 return totalBytesRetired + bufferPos;
713 * Called with {@code this.buffer} is empty to read more bytes from the
714 * input. If {@code mustSucceed} is true, refillBuffer() guarantees that
715 * either there will be at least one byte in the buffer when it returns
716 * or it will throw an exception. If {@code mustSucceed} is false,
717 * refillBuffer() returns false if no more bytes were available.
719 private boolean refillBuffer(final boolean mustSucceed) throws IOException {
720 if (bufferPos < bufferSize) {
721 throw new IllegalStateException(
722 "refillBuffer() called when buffer wasn't empty.");
725 if (totalBytesRetired + bufferSize == currentLimit) {
726 // Oops, we hit a limit.
728 throw InvalidProtocolBufferException.truncatedMessage();
734 totalBytesRetired += bufferSize;
737 bufferSize = (input == null) ? -1 : input.read(buffer);
738 if (bufferSize == 0 || bufferSize < -1) {
739 throw new IllegalStateException(
740 "InputStream#read(byte[]) returned invalid result: " + bufferSize +
741 "\nThe InputStream implementation is buggy.");
743 if (bufferSize == -1) {
746 throw InvalidProtocolBufferException.truncatedMessage();
751 recomputeBufferSizeAfterLimit();
752 final int totalBytesRead =
753 totalBytesRetired + bufferSize + bufferSizeAfterLimit;
754 if (totalBytesRead > sizeLimit || totalBytesRead < 0) {
755 throw InvalidProtocolBufferException.sizeLimitExceeded();
762 * Read one byte from the input.
764 * @throws InvalidProtocolBufferException The end of the stream or the current
767 public byte readRawByte() throws IOException {
768 if (bufferPos == bufferSize) {
771 return buffer[bufferPos++];
775 * Read a fixed size of bytes from the input.
777 * @throws InvalidProtocolBufferException The end of the stream or the current
780 public byte[] readRawBytes(final int size) throws IOException {
782 throw InvalidProtocolBufferException.negativeSize();
785 if (totalBytesRetired + bufferPos + size > currentLimit) {
786 // Read to the end of the stream anyway.
787 skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
789 throw InvalidProtocolBufferException.truncatedMessage();
792 if (size <= bufferSize - bufferPos) {
793 // We have all the bytes we need already.
794 final byte[] bytes = new byte[size];
795 System.arraycopy(buffer, bufferPos, bytes, 0, size);
798 } else if (size < BUFFER_SIZE) {
799 // Reading more bytes than are in the buffer, but not an excessive number
800 // of bytes. We can safely allocate the resulting array ahead of time.
802 // First copy what we have.
803 final byte[] bytes = new byte[size];
804 int pos = bufferSize - bufferPos;
805 System.arraycopy(buffer, bufferPos, bytes, 0, pos);
806 bufferPos = bufferSize;
808 // We want to use refillBuffer() and then copy from the buffer into our
809 // byte array rather than reading directly into our byte array because
810 // the input may be unbuffered.
813 while (size - pos > bufferSize) {
814 System.arraycopy(buffer, 0, bytes, pos, bufferSize);
816 bufferPos = bufferSize;
820 System.arraycopy(buffer, 0, bytes, pos, size - pos);
821 bufferPos = size - pos;
825 // The size is very large. For security reasons, we can't allocate the
826 // entire byte array yet. The size comes directly from the input, so a
827 // maliciously-crafted message could provide a bogus very large size in
828 // order to trick the app into allocating a lot of memory. We avoid this
829 // by allocating and reading only a small chunk at a time, so that the
830 // malicious message must actually *be* extremely large to cause
831 // problems. Meanwhile, we limit the allowed size of a message elsewhere.
833 // Remember the buffer markers since we'll have to copy the bytes out of
835 final int originalBufferPos = bufferPos;
836 final int originalBufferSize = bufferSize;
838 // Mark the current buffer consumed.
839 totalBytesRetired += bufferSize;
843 // Read all the rest of the bytes we need.
844 int sizeLeft = size - (originalBufferSize - originalBufferPos);
845 final List<byte[]> chunks = new ArrayList<byte[]>();
847 while (sizeLeft > 0) {
848 final byte[] chunk = new byte[Math.min(sizeLeft, BUFFER_SIZE)];
850 while (pos < chunk.length) {
851 final int n = (input == null) ? -1 :
852 input.read(chunk, pos, chunk.length - pos);
854 throw InvalidProtocolBufferException.truncatedMessage();
856 totalBytesRetired += n;
859 sizeLeft -= chunk.length;
863 // OK, got everything. Now concatenate it all into one buffer.
864 final byte[] bytes = new byte[size];
866 // Start by copying the leftover bytes from this.buffer.
867 int pos = originalBufferSize - originalBufferPos;
868 System.arraycopy(buffer, originalBufferPos, bytes, 0, pos);
870 // And now all the chunks.
871 for (final byte[] chunk : chunks) {
872 System.arraycopy(chunk, 0, bytes, pos, chunk.length);
882 * Reads and discards {@code size} bytes.
884 * @throws InvalidProtocolBufferException The end of the stream or the current
887 public void skipRawBytes(final int size) throws IOException {
889 throw InvalidProtocolBufferException.negativeSize();
892 if (totalBytesRetired + bufferPos + size > currentLimit) {
893 // Read to the end of the stream anyway.
894 skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
896 throw InvalidProtocolBufferException.truncatedMessage();
899 if (size <= bufferSize - bufferPos) {
900 // We have all the bytes we need already.
903 // Skipping more bytes than are in the buffer. First skip what we have.
904 int pos = bufferSize - bufferPos;
905 bufferPos = bufferSize;
907 // Keep refilling the buffer until we get to the point we wanted to skip
908 // to. This has the side effect of ensuring the limits are updated
911 while (size - pos > bufferSize) {
913 bufferPos = bufferSize;
917 bufferPos = size - pos;