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