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