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