1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.util.proto; 18 19 import android.util.LongArray; 20 21 import java.io.IOException; 22 import java.io.InputStream; 23 import java.nio.charset.StandardCharsets; 24 import java.util.Arrays; 25 import java.util.Objects; 26 27 /** 28 * Class to read to a protobuf stream. 29 * 30 * Each read method takes an ID code from the protoc generated classes 31 * and return a value of the field. To read a nested object, call #start 32 * and then #end when you are done. 33 * 34 * The ID codes have type information embedded into them, so if you call 35 * the incorrect function you will get an IllegalArgumentException. 36 * 37 * nextField will return the field number of the next field, which can be 38 * matched to the protoc generated ID code and used to determine how to 39 * read the next field. 40 * 41 * It is STRONGLY RECOMMENDED to read from the ProtoInputStream with a switch 42 * statement wrapped in a while loop. Additionally, it is worth logging or 43 * storing unexpected fields or ones that do not match the expected wire type 44 * 45 * ex: 46 * void parseFromProto(ProtoInputStream stream) { 47 * while(stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { 48 * try { 49 * switch (stream.getFieldNumber()) { 50 * case (int) DummyProto.NAME: 51 * mName = stream.readString(DummyProto.NAME); 52 * break; 53 * case (int) DummyProto.VALUE: 54 * mValue = stream.readInt(DummyProto.VALUE); 55 * break; 56 * default: 57 * LOG(TAG, "Unhandled field in proto!\n" 58 * + ProtoUtils.currentFieldToString(stream)); 59 * } 60 * } catch (WireTypeMismatchException wtme) { 61 * LOG(TAG, "Wire Type mismatch in proto!\n" + ProtoUtils.currentFieldToString(stream)); 62 * } 63 * } 64 * } 65 * 66 * @hide 67 */ 68 @android.ravenwood.annotation.RavenwoodKeepWholeClass 69 public final class ProtoInputStream extends ProtoStream { 70 71 public static final int NO_MORE_FIELDS = -1; 72 73 /** 74 * Our stream. If there is one. 75 */ 76 private InputStream mStream; 77 78 /** 79 * The field number of the current field. Will be equal to NO_MORE_FIELDS if end of message is 80 * reached 81 */ 82 private int mFieldNumber; 83 84 /** 85 * The wire type of the current field 86 */ 87 private int mWireType; 88 89 private static final byte STATE_STARTED_FIELD_READ = 1 << 0; 90 private static final byte STATE_READING_PACKED = 1 << 1; 91 private static final byte STATE_FIELD_MISS = 2 << 1; 92 93 /** 94 * Tracks some boolean states for the proto input stream 95 * bit 0: Started Field Read, true - tag has been read, ready to read field data. 96 * false - field data has been read, reading to start next field. 97 * bit 1: Reading Packed Field, true - currently reading values from a packed field 98 * false - not reading from packed field. 99 */ 100 private byte mState = 0; 101 102 /** 103 * Keeps track of the currently read nested Objects, for end object checking and debug 104 */ 105 private LongArray mExpectedObjectTokenStack = null; 106 107 /** 108 * Current nesting depth of start calls. 109 */ 110 private int mDepth = -1; 111 112 /** 113 * Buffer for the to be read data. If mStream is not null, it will be constantly refilled from 114 * the stream. 115 */ 116 private byte[] mBuffer; 117 118 private static final int DEFAULT_BUFFER_SIZE = 8192; 119 120 /** 121 * Size of the buffer if reading from a stream. 122 */ 123 private final int mBufferSize; 124 125 /** 126 * The number of bytes that have been skipped or dropped from the buffer. 127 */ 128 private int mDiscardedBytes = 0; 129 130 /** 131 * Current offset in the buffer 132 * mOffset + mDiscardedBytes = current offset in proto binary 133 */ 134 private int mOffset = 0; 135 136 /** 137 * Note the offset of the last byte in the buffer. Usually will equal the size of the buffer. 138 * mEnd + mDiscardedBytes = the last known byte offset + 1 139 */ 140 private int mEnd = 0; 141 142 /** 143 * Packed repeated fields are not read in one go. mPackedEnd keeps track of where the packed 144 * field ends in the proto binary if current field is packed. 145 */ 146 private int mPackedEnd = 0; 147 148 /** 149 * Construct a ProtoInputStream on top of an InputStream to read a proto. Also specify the 150 * number of bytes the ProtoInputStream will buffer from the input stream 151 * 152 * @param stream from which the proto is read 153 */ ProtoInputStream(InputStream stream, int bufferSize)154 public ProtoInputStream(InputStream stream, int bufferSize) { 155 mStream = stream; 156 if (bufferSize > 0) { 157 mBufferSize = bufferSize; 158 } else { 159 mBufferSize = DEFAULT_BUFFER_SIZE; 160 } 161 mBuffer = new byte[mBufferSize]; 162 } 163 164 /** 165 * Construct a ProtoInputStream on top of an InputStream to read a proto 166 * 167 * @param stream from which the proto is read 168 */ ProtoInputStream(InputStream stream)169 public ProtoInputStream(InputStream stream) { 170 this(stream, DEFAULT_BUFFER_SIZE); 171 } 172 173 /** 174 * Construct a ProtoInputStream to read a proto directly from a byte array 175 * 176 * @param buffer - the byte array to be parsed 177 */ ProtoInputStream(byte[] buffer)178 public ProtoInputStream(byte[] buffer) { 179 mBufferSize = buffer.length; 180 mEnd = buffer.length; 181 mBuffer = buffer; 182 mStream = null; 183 } 184 185 /** 186 * Get the field number of the current field. 187 */ getFieldNumber()188 public int getFieldNumber() { 189 return mFieldNumber; 190 } 191 192 /** 193 * Get the wire type of the current field. 194 * 195 * @return an int that matches one of the ProtoStream WIRE_TYPE_ constants 196 */ getWireType()197 public int getWireType() { 198 if ((mState & STATE_READING_PACKED) == STATE_READING_PACKED) { 199 // mWireType got overwritten when STATE_READING_PACKED was set. Send length delimited 200 // constant instead 201 return WIRE_TYPE_LENGTH_DELIMITED; 202 } 203 return mWireType; 204 } 205 206 /** 207 * Get the current offset in the proto binary. 208 */ getOffset()209 public int getOffset() { 210 return mOffset + mDiscardedBytes; 211 } 212 213 /** 214 * Reads the tag of the next field from the stream. If previous field value was not read, its 215 * data will be skipped over. 216 * 217 * @return the field number of the next field 218 * @throws IOException if an I/O error occurs 219 */ nextField()220 public int nextField() throws IOException { 221 222 if ((mState & STATE_FIELD_MISS) == STATE_FIELD_MISS) { 223 // Data from the last nextField was not used, reuse the info 224 mState &= ~STATE_FIELD_MISS; 225 return mFieldNumber; 226 } 227 if ((mState & STATE_STARTED_FIELD_READ) == STATE_STARTED_FIELD_READ) { 228 // Field data was not read, skip to the next field 229 skip(); 230 mState &= ~STATE_STARTED_FIELD_READ; 231 } 232 if ((mState & STATE_READING_PACKED) == STATE_READING_PACKED) { 233 if (getOffset() < mPackedEnd) { 234 // In the middle of a packed field, return the same tag until last packed value 235 // has been read 236 mState |= STATE_STARTED_FIELD_READ; 237 return mFieldNumber; 238 } else if (getOffset() == mPackedEnd) { 239 // Reached the end of the packed field 240 mState &= ~STATE_READING_PACKED; 241 } else { 242 throw new ProtoParseException( 243 "Unexpectedly reached end of packed field at offset 0x" 244 + Integer.toHexString(mPackedEnd) 245 + dumpDebugData()); 246 } 247 } 248 249 if ((mDepth >= 0) && (getOffset() == getOffsetFromToken( 250 mExpectedObjectTokenStack.get(mDepth)))) { 251 // reached end of a embedded message 252 mFieldNumber = NO_MORE_FIELDS; 253 } else { 254 readTag(); 255 } 256 return mFieldNumber; 257 } 258 259 /** 260 * Reads the tag of the next field from the stream. If previous field value was not read, its 261 * data will be skipped over. If {@code fieldId} matches the next field ID, the field data will 262 * be ready to read. If it does not match, {@link #nextField()} or {@link #nextField(long)} will 263 * need to be called again before the field data can be read. 264 * 265 * @return true if fieldId matches the next field, false if not 266 */ nextField(long fieldId)267 public boolean nextField(long fieldId) throws IOException { 268 if (nextField() == (int) fieldId) { 269 return true; 270 } 271 // Note to reuse the info from the nextField call in the next call. 272 mState |= STATE_FIELD_MISS; 273 return false; 274 } 275 276 /** 277 * Read a single double. 278 * Will throw if the current wire type is not fixed64 279 * 280 * @param fieldId - must match the current field number and field type 281 */ readDouble(long fieldId)282 public double readDouble(long fieldId) throws IOException { 283 assertFreshData(); 284 assertFieldNumber(fieldId); 285 checkPacked(fieldId); 286 287 double value; 288 switch ((int) ((fieldId & FIELD_TYPE_MASK) 289 >>> FIELD_TYPE_SHIFT)) { 290 case (int) (FIELD_TYPE_DOUBLE >>> FIELD_TYPE_SHIFT): 291 assertWireType(WIRE_TYPE_FIXED64); 292 value = Double.longBitsToDouble(readFixed64()); 293 break; 294 default: 295 throw new IllegalArgumentException( 296 "Requested field id (" + getFieldIdString(fieldId) 297 + ") cannot be read as a double" 298 + dumpDebugData()); 299 } 300 // Successfully read the field 301 mState &= ~STATE_STARTED_FIELD_READ; 302 return value; 303 } 304 305 /** 306 * Read a single float. 307 * Will throw if the current wire type is not fixed32 308 * 309 * @param fieldId - must match the current field number and field type 310 */ readFloat(long fieldId)311 public float readFloat(long fieldId) throws IOException { 312 assertFreshData(); 313 assertFieldNumber(fieldId); 314 checkPacked(fieldId); 315 316 float value; 317 switch ((int) ((fieldId & FIELD_TYPE_MASK) 318 >>> FIELD_TYPE_SHIFT)) { 319 case (int) (FIELD_TYPE_FLOAT >>> FIELD_TYPE_SHIFT): 320 assertWireType(WIRE_TYPE_FIXED32); 321 value = Float.intBitsToFloat(readFixed32()); 322 break; 323 default: 324 throw new IllegalArgumentException( 325 "Requested field id (" + getFieldIdString(fieldId) + ") is not a float" 326 + dumpDebugData()); 327 } 328 // Successfully read the field 329 mState &= ~STATE_STARTED_FIELD_READ; 330 return value; 331 } 332 333 /** 334 * Read a single 32bit or varint proto type field as an int. 335 * Will throw if the current wire type is not varint or fixed32 336 * 337 * @param fieldId - must match the current field number and field type 338 */ readInt(long fieldId)339 public int readInt(long fieldId) throws IOException { 340 assertFreshData(); 341 assertFieldNumber(fieldId); 342 checkPacked(fieldId); 343 344 int value; 345 switch ((int) ((fieldId & FIELD_TYPE_MASK) 346 >>> FIELD_TYPE_SHIFT)) { 347 case (int) (FIELD_TYPE_FIXED32 >>> FIELD_TYPE_SHIFT): 348 case (int) (FIELD_TYPE_SFIXED32 >>> FIELD_TYPE_SHIFT): 349 assertWireType(WIRE_TYPE_FIXED32); 350 value = readFixed32(); 351 break; 352 case (int) (FIELD_TYPE_SINT32 >>> FIELD_TYPE_SHIFT): 353 assertWireType(WIRE_TYPE_VARINT); 354 value = decodeZigZag32((int) readVarint()); 355 break; 356 case (int) (FIELD_TYPE_INT32 >>> FIELD_TYPE_SHIFT): 357 case (int) (FIELD_TYPE_UINT32 >>> FIELD_TYPE_SHIFT): 358 case (int) (FIELD_TYPE_ENUM >>> FIELD_TYPE_SHIFT): 359 assertWireType(WIRE_TYPE_VARINT); 360 value = (int) readVarint(); 361 break; 362 default: 363 throw new IllegalArgumentException( 364 "Requested field id (" + getFieldIdString(fieldId) + ") is not an int" 365 + dumpDebugData()); 366 } 367 // Successfully read the field 368 mState &= ~STATE_STARTED_FIELD_READ; 369 return value; 370 } 371 372 /** 373 * Read a single 64bit or varint proto type field as an long. 374 * 375 * @param fieldId - must match the current field number 376 */ readLong(long fieldId)377 public long readLong(long fieldId) throws IOException { 378 assertFreshData(); 379 assertFieldNumber(fieldId); 380 checkPacked(fieldId); 381 382 long value; 383 switch ((int) ((fieldId & FIELD_TYPE_MASK) 384 >>> FIELD_TYPE_SHIFT)) { 385 case (int) (FIELD_TYPE_FIXED64 >>> FIELD_TYPE_SHIFT): 386 case (int) (FIELD_TYPE_SFIXED64 >>> FIELD_TYPE_SHIFT): 387 assertWireType(WIRE_TYPE_FIXED64); 388 value = readFixed64(); 389 break; 390 case (int) (FIELD_TYPE_SINT64 >>> FIELD_TYPE_SHIFT): 391 assertWireType(WIRE_TYPE_VARINT); 392 value = decodeZigZag64(readVarint()); 393 break; 394 case (int) (FIELD_TYPE_INT64 >>> FIELD_TYPE_SHIFT): 395 case (int) (FIELD_TYPE_UINT64 >>> FIELD_TYPE_SHIFT): 396 assertWireType(WIRE_TYPE_VARINT); 397 value = readVarint(); 398 break; 399 default: 400 throw new IllegalArgumentException( 401 "Requested field id (" + getFieldIdString(fieldId) + ") is not an long" 402 + dumpDebugData()); 403 } 404 // Successfully read the field 405 mState &= ~STATE_STARTED_FIELD_READ; 406 return value; 407 } 408 409 /** 410 * Read a single 32bit or varint proto type field as an boolean. 411 * 412 * @param fieldId - must match the current field number 413 */ readBoolean(long fieldId)414 public boolean readBoolean(long fieldId) throws IOException { 415 assertFreshData(); 416 assertFieldNumber(fieldId); 417 checkPacked(fieldId); 418 419 boolean value; 420 switch ((int) ((fieldId & FIELD_TYPE_MASK) 421 >>> FIELD_TYPE_SHIFT)) { 422 case (int) (FIELD_TYPE_BOOL >>> FIELD_TYPE_SHIFT): 423 assertWireType(WIRE_TYPE_VARINT); 424 value = readVarint() != 0; 425 break; 426 default: 427 throw new IllegalArgumentException( 428 "Requested field id (" + getFieldIdString(fieldId) + ") is not an boolean" 429 + dumpDebugData()); 430 } 431 // Successfully read the field 432 mState &= ~STATE_STARTED_FIELD_READ; 433 return value; 434 } 435 436 /** 437 * Read a string field 438 * 439 * @param fieldId - must match the current field number 440 */ readString(long fieldId)441 public String readString(long fieldId) throws IOException { 442 assertFreshData(); 443 assertFieldNumber(fieldId); 444 445 String value; 446 switch ((int) ((fieldId & FIELD_TYPE_MASK) >>> FIELD_TYPE_SHIFT)) { 447 case (int) (FIELD_TYPE_STRING >>> FIELD_TYPE_SHIFT): 448 assertWireType(WIRE_TYPE_LENGTH_DELIMITED); 449 int len = (int) readVarint(); 450 value = readRawString(len); 451 break; 452 default: 453 throw new IllegalArgumentException( 454 "Requested field id(" + getFieldIdString(fieldId) 455 + ") is not an string" 456 + dumpDebugData()); 457 } 458 // Successfully read the field 459 mState &= ~STATE_STARTED_FIELD_READ; 460 return value; 461 } 462 463 /** 464 * Read a bytes field 465 * 466 * @param fieldId - must match the current field number 467 */ readBytes(long fieldId)468 public byte[] readBytes(long fieldId) throws IOException { 469 assertFreshData(); 470 assertFieldNumber(fieldId); 471 472 byte[] value; 473 switch ((int) ((fieldId & FIELD_TYPE_MASK) >>> FIELD_TYPE_SHIFT)) { 474 case (int) (FIELD_TYPE_MESSAGE >>> FIELD_TYPE_SHIFT): 475 case (int) (FIELD_TYPE_BYTES >>> FIELD_TYPE_SHIFT): 476 assertWireType(WIRE_TYPE_LENGTH_DELIMITED); 477 int len = (int) readVarint(); 478 value = readRawBytes(len); 479 break; 480 default: 481 throw new IllegalArgumentException( 482 "Requested field type (" + getFieldIdString(fieldId) 483 + ") cannot be read as raw bytes" 484 + dumpDebugData()); 485 } 486 // Successfully read the field 487 mState &= ~STATE_STARTED_FIELD_READ; 488 return value; 489 } 490 491 /** 492 * Start the read of an embedded Object 493 * 494 * @param fieldId - must match the current field number 495 * @return a token. The token must be handed back when finished reading embedded Object 496 */ start(long fieldId)497 public long start(long fieldId) throws IOException { 498 assertFreshData(); 499 assertFieldNumber(fieldId); 500 assertWireType(WIRE_TYPE_LENGTH_DELIMITED); 501 502 int messageSize = (int) readVarint(); 503 504 if (mExpectedObjectTokenStack == null) { 505 mExpectedObjectTokenStack = new LongArray(); 506 } 507 if (++mDepth == mExpectedObjectTokenStack.size()) { 508 // Create a token to keep track of nested Object and extend the object stack 509 mExpectedObjectTokenStack.add(makeToken(0, 510 (fieldId & FIELD_COUNT_REPEATED) == FIELD_COUNT_REPEATED, mDepth, 511 (int) fieldId, getOffset() + messageSize)); 512 513 } else { 514 // Create a token to keep track of nested Object 515 mExpectedObjectTokenStack.set(mDepth, makeToken(0, 516 (fieldId & FIELD_COUNT_REPEATED) == FIELD_COUNT_REPEATED, mDepth, 517 (int) fieldId, getOffset() + messageSize)); 518 } 519 520 // Validation check 521 if (mDepth > 0 522 && getOffsetFromToken(mExpectedObjectTokenStack.get(mDepth)) 523 > getOffsetFromToken(mExpectedObjectTokenStack.get(mDepth - 1))) { 524 throw new ProtoParseException("Embedded Object (" 525 + token2String(mExpectedObjectTokenStack.get(mDepth)) 526 + ") ends after of parent Objects's (" 527 + token2String(mExpectedObjectTokenStack.get(mDepth - 1)) 528 + ") end" 529 + dumpDebugData()); 530 } 531 mState &= ~STATE_STARTED_FIELD_READ; 532 return mExpectedObjectTokenStack.get(mDepth); 533 } 534 535 /** 536 * Note the end of a nested object. Must be called to continue streaming the rest of the proto. 537 * end can be called mid object parse. The offset will be moved to the next field outside the 538 * object. 539 * 540 * @param token - token 541 */ end(long token)542 public void end(long token) { 543 // Make sure user is keeping track of their embedded messages 544 if (mExpectedObjectTokenStack.get(mDepth) != token) { 545 throw new ProtoParseException( 546 "end token " + token + " does not match current message token " 547 + mExpectedObjectTokenStack.get(mDepth) 548 + dumpDebugData()); 549 } 550 if (getOffsetFromToken(mExpectedObjectTokenStack.get(mDepth)) > getOffset()) { 551 // Did not read all of the message, skip to the end 552 incOffset(getOffsetFromToken(mExpectedObjectTokenStack.get(mDepth)) - getOffset()); 553 } 554 mDepth--; 555 mState &= ~STATE_STARTED_FIELD_READ; 556 } 557 558 /** 559 * Read the tag at the start of the next field and collect field number and wire type. 560 * Will set mFieldNumber to NO_MORE_FIELDS if end of buffer/stream reached. 561 */ readTag()562 private void readTag() throws IOException { 563 fillBuffer(); 564 if (mOffset >= mEnd) { 565 // reached end of the stream 566 mFieldNumber = NO_MORE_FIELDS; 567 return; 568 } 569 int tag = (int) readVarint(); 570 mFieldNumber = tag >>> FIELD_ID_SHIFT; 571 mWireType = tag & WIRE_TYPE_MASK; 572 mState |= STATE_STARTED_FIELD_READ; 573 } 574 575 /** 576 * Decode a 32 bit ZigZag encoded signed int. 577 * 578 * @param n - int to decode 579 * @return the decoded signed int 580 */ decodeZigZag32(final int n)581 public int decodeZigZag32(final int n) { 582 return (n >>> 1) ^ -(n & 1); 583 } 584 585 /** 586 * Decode a 64 bit ZigZag encoded signed long. 587 * 588 * @param n - long to decode 589 * @return the decoded signed long 590 */ decodeZigZag64(final long n)591 public long decodeZigZag64(final long n) { 592 return (n >>> 1) ^ -(n & 1); 593 } 594 595 /** 596 * Read a varint from the buffer 597 * 598 * @return the varint as a long 599 */ readVarint()600 private long readVarint() throws IOException { 601 long value = 0; 602 int shift = 0; 603 while (true) { 604 fillBuffer(); 605 // Limit how much bookkeeping is done by checking how far away the end of the buffer is 606 // and directly accessing buffer up until the end. 607 final int fragment = mEnd - mOffset; 608 if (fragment < 0) { 609 throw new ProtoParseException( 610 "Incomplete varint at offset 0x" 611 + Integer.toHexString(getOffset()) 612 + dumpDebugData()); 613 } 614 for (int i = 0; i < fragment; i++) { 615 byte b = mBuffer[(mOffset + i)]; 616 value |= (b & 0x7FL) << shift; 617 if ((b & 0x80) == 0) { 618 incOffset(i + 1); 619 return value; 620 } 621 shift += 7; 622 if (shift > 63) { 623 throw new ProtoParseException( 624 "Varint is too large at offset 0x" 625 + Integer.toHexString(getOffset() + i) 626 + dumpDebugData()); 627 } 628 } 629 // Hit the end of the buffer, do some incrementing and checking, then continue 630 incOffset(fragment); 631 } 632 } 633 634 /** 635 * Read a fixed 32 bit int from the buffer 636 * 637 * @return the fixed32 as a int 638 */ readFixed32()639 private int readFixed32() throws IOException { 640 // check for fast path, which is likely with a reasonable buffer size 641 if (mOffset + 4 <= mEnd) { 642 // don't bother filling buffer since we know the end is plenty far away 643 incOffset(4); 644 return (mBuffer[mOffset - 4] & 0xFF) 645 | ((mBuffer[mOffset - 3] & 0xFF) << 8) 646 | ((mBuffer[mOffset - 2] & 0xFF) << 16) 647 | ((mBuffer[mOffset - 1] & 0xFF) << 24); 648 } 649 650 // the Fixed32 crosses the edge of a chunk, read the Fixed32 in multiple fragments. 651 // There will be two fragment reads except when the chunk size is 2 or less. 652 int value = 0; 653 int shift = 0; 654 int bytesLeft = 4; 655 while (bytesLeft > 0) { 656 fillBuffer(); 657 // Find the number of bytes available until the end of the chunk or Fixed32 658 int fragment = (mEnd - mOffset) < bytesLeft ? (mEnd - mOffset) : bytesLeft; 659 if (fragment < 0) { 660 throw new ProtoParseException( 661 "Incomplete fixed32 at offset 0x" 662 + Integer.toHexString(getOffset()) 663 + dumpDebugData()); 664 } 665 incOffset(fragment); 666 bytesLeft -= fragment; 667 while (fragment > 0) { 668 value |= ((mBuffer[mOffset - fragment] & 0xFF) << shift); 669 fragment--; 670 shift += 8; 671 } 672 } 673 return value; 674 } 675 676 /** 677 * Read a fixed 64 bit long from the buffer 678 * 679 * @return the fixed64 as a long 680 */ 681 private long readFixed64() throws IOException { 682 // check for fast path, which is likely with a reasonable buffer size 683 if (mOffset + 8 <= mEnd) { 684 // don't bother filling buffer since we know the end is plenty far away 685 incOffset(8); 686 return (mBuffer[mOffset - 8] & 0xFFL) 687 | ((mBuffer[mOffset - 7] & 0xFFL) << 8) 688 | ((mBuffer[mOffset - 6] & 0xFFL) << 16) 689 | ((mBuffer[mOffset - 5] & 0xFFL) << 24) 690 | ((mBuffer[mOffset - 4] & 0xFFL) << 32) 691 | ((mBuffer[mOffset - 3] & 0xFFL) << 40) 692 | ((mBuffer[mOffset - 2] & 0xFFL) << 48) 693 | ((mBuffer[mOffset - 1] & 0xFFL) << 56); 694 } 695 696 // the Fixed64 crosses the edge of a chunk, read the Fixed64 in multiple fragments. 697 // There will be two fragment reads except when the chunk size is 6 or less. 698 long value = 0; 699 int shift = 0; 700 int bytesLeft = 8; 701 while (bytesLeft > 0) { 702 fillBuffer(); 703 // Find the number of bytes available until the end of the chunk or Fixed64 704 int fragment = (mEnd - mOffset) < bytesLeft ? (mEnd - mOffset) : bytesLeft; 705 if (fragment < 0) { 706 throw new ProtoParseException( 707 "Incomplete fixed64 at offset 0x" 708 + Integer.toHexString(getOffset()) 709 + dumpDebugData()); 710 } 711 incOffset(fragment); 712 bytesLeft -= fragment; 713 while (fragment > 0) { 714 value |= ((mBuffer[(mOffset - fragment)] & 0xFFL) << shift); 715 fragment--; 716 shift += 8; 717 } 718 } 719 return value; 720 } 721 722 /** 723 * Read raw bytes from the buffer 724 * 725 * @param n - number of bytes to read 726 * @return a byte array with raw bytes 727 */ 728 private byte[] readRawBytes(int n) throws IOException { 729 byte[] buffer = new byte[n]; 730 int pos = 0; 731 while (mOffset + n - pos > mEnd) { 732 int fragment = mEnd - mOffset; 733 if (fragment > 0) { 734 System.arraycopy(mBuffer, mOffset, buffer, pos, fragment); 735 incOffset(fragment); 736 pos += fragment; 737 } 738 fillBuffer(); 739 if (mOffset >= mEnd) { 740 throw new ProtoParseException( 741 "Unexpectedly reached end of the InputStream at offset 0x" 742 + Integer.toHexString(mEnd) 743 + dumpDebugData()); 744 } 745 } 746 System.arraycopy(mBuffer, mOffset, buffer, pos, n - pos); 747 incOffset(n - pos); 748 return buffer; 749 } 750 751 /** 752 * Read raw string from the buffer 753 * 754 * @param n - number of bytes to read 755 * @return a string 756 */ 757 private String readRawString(int n) throws IOException { 758 fillBuffer(); 759 if (mOffset + n <= mEnd) { 760 // fast path read. String is well within the current buffer 761 String value = new String(mBuffer, mOffset, n, StandardCharsets.UTF_8); 762 incOffset(n); 763 return value; 764 } else if (n <= mBufferSize) { 765 // String extends past buffer, but can be encapsulated in a buffer. Copy the first chunk 766 // of the string to the start of the buffer and then fill the rest of the buffer from 767 // the stream. 768 final int stringHead = mEnd - mOffset; 769 System.arraycopy(mBuffer, mOffset, mBuffer, 0, stringHead); 770 mEnd = stringHead + mStream.read(mBuffer, stringHead, n - stringHead); 771 772 mDiscardedBytes += mOffset; 773 mOffset = 0; 774 775 String value = new String(mBuffer, mOffset, n, StandardCharsets.UTF_8); 776 incOffset(n); 777 return value; 778 } 779 // Otherwise, the string is too large to use the buffer. Create the string from a 780 // separate byte array. 781 return new String(readRawBytes(n), 0, n, StandardCharsets.UTF_8); 782 } 783 784 /** 785 * Fill the buffer with a chunk from the stream if need be. 786 * Will skip chunks until mOffset is reached 787 */ 788 private void fillBuffer() throws IOException { 789 if (mOffset >= mEnd && mStream != null) { 790 mOffset -= mEnd; 791 mDiscardedBytes += mEnd; 792 if (mOffset >= mBufferSize) { 793 int skipped = (int) mStream.skip((mOffset / mBufferSize) * mBufferSize); 794 mDiscardedBytes += skipped; 795 mOffset -= skipped; 796 } 797 mEnd = mStream.read(mBuffer); 798 } 799 } 800 801 /** 802 * Skips the rest of current field and moves to the start of the next field. This should only be 803 * called while state is STATE_STARTED_FIELD_READ 804 */ 805 public void skip() throws IOException { 806 if ((mState & STATE_READING_PACKED) == STATE_READING_PACKED) { 807 incOffset(mPackedEnd - getOffset()); 808 } else { 809 switch (mWireType) { 810 case WIRE_TYPE_VARINT: 811 byte b; 812 do { 813 fillBuffer(); 814 b = mBuffer[mOffset]; 815 incOffset(1); 816 } while ((b & 0x80) != 0); 817 break; 818 case WIRE_TYPE_FIXED64: 819 incOffset(8); 820 break; 821 case WIRE_TYPE_LENGTH_DELIMITED: 822 fillBuffer(); 823 int length = (int) readVarint(); 824 incOffset(length); 825 break; 826 /* 827 case WIRE_TYPE_START_GROUP: 828 // Not implemented 829 break; 830 case WIRE_TYPE_END_GROUP: 831 // Not implemented 832 break; 833 */ 834 case WIRE_TYPE_FIXED32: 835 incOffset(4); 836 break; 837 default: 838 throw new ProtoParseException( 839 "Unexpected wire type: " + mWireType + " at offset 0x" 840 + Integer.toHexString(mOffset) 841 + dumpDebugData()); 842 } 843 } 844 mState &= ~STATE_STARTED_FIELD_READ; 845 } 846 847 /** 848 * Increment the offset and handle all the relevant bookkeeping 849 * Refilling the buffer when its end is reached will be handled elsewhere (ideally just before 850 * a read, to avoid unnecessary reads from stream) 851 * 852 * @param n - number of bytes to increment 853 */ 854 private void incOffset(int n) { 855 mOffset += n; 856 857 if (mDepth >= 0 && getOffset() > getOffsetFromToken( 858 mExpectedObjectTokenStack.get(mDepth))) { 859 throw new ProtoParseException("Unexpectedly reached end of embedded object. " 860 + token2String(mExpectedObjectTokenStack.get(mDepth)) 861 + dumpDebugData()); 862 } 863 } 864 865 /** 866 * Check the current wire type to determine if current numeric field is packed. If it is packed, 867 * set up to deal with the field 868 * This should only be called for primitive numeric field types. 869 * 870 * @param fieldId - used to determine what the packed wire type is. 871 */ 872 private void checkPacked(long fieldId) throws IOException { 873 if (mWireType == WIRE_TYPE_LENGTH_DELIMITED) { 874 // Primitive Field is length delimited, must be a packed field. 875 final int length = (int) readVarint(); 876 mPackedEnd = getOffset() + length; 877 mState |= STATE_READING_PACKED; 878 879 // Fake the wire type, based on the field type 880 switch ((int) ((fieldId & FIELD_TYPE_MASK) 881 >>> FIELD_TYPE_SHIFT)) { 882 case (int) (FIELD_TYPE_FLOAT >>> FIELD_TYPE_SHIFT): 883 case (int) (FIELD_TYPE_FIXED32 >>> FIELD_TYPE_SHIFT): 884 case (int) (FIELD_TYPE_SFIXED32 >>> FIELD_TYPE_SHIFT): 885 if (length % 4 != 0) { 886 throw new IllegalArgumentException( 887 "Requested field id (" + getFieldIdString(fieldId) 888 + ") packed length " + length 889 + " is not aligned for fixed32" 890 + dumpDebugData()); 891 } 892 mWireType = WIRE_TYPE_FIXED32; 893 break; 894 case (int) (FIELD_TYPE_DOUBLE >>> FIELD_TYPE_SHIFT): 895 case (int) (FIELD_TYPE_FIXED64 >>> FIELD_TYPE_SHIFT): 896 case (int) (FIELD_TYPE_SFIXED64 >>> FIELD_TYPE_SHIFT): 897 if (length % 8 != 0) { 898 throw new IllegalArgumentException( 899 "Requested field id (" + getFieldIdString(fieldId) 900 + ") packed length " + length 901 + " is not aligned for fixed64" 902 + dumpDebugData()); 903 } 904 mWireType = WIRE_TYPE_FIXED64; 905 break; 906 case (int) (FIELD_TYPE_SINT32 >>> FIELD_TYPE_SHIFT): 907 case (int) (FIELD_TYPE_INT32 >>> FIELD_TYPE_SHIFT): 908 case (int) (FIELD_TYPE_UINT32 >>> FIELD_TYPE_SHIFT): 909 case (int) (FIELD_TYPE_SINT64 >>> FIELD_TYPE_SHIFT): 910 case (int) (FIELD_TYPE_INT64 >>> FIELD_TYPE_SHIFT): 911 case (int) (FIELD_TYPE_UINT64 >>> FIELD_TYPE_SHIFT): 912 case (int) (FIELD_TYPE_ENUM >>> FIELD_TYPE_SHIFT): 913 case (int) (FIELD_TYPE_BOOL >>> FIELD_TYPE_SHIFT): 914 mWireType = WIRE_TYPE_VARINT; 915 break; 916 default: 917 throw new IllegalArgumentException( 918 "Requested field id (" + getFieldIdString(fieldId) 919 + ") is not a packable field" 920 + dumpDebugData()); 921 } 922 } 923 } 924 925 926 /** 927 * Check a field id constant against current field number 928 * 929 * @param fieldId - throws if fieldId does not match mFieldNumber 930 */ assertFieldNumber(long fieldId)931 private void assertFieldNumber(long fieldId) { 932 if ((int) fieldId != mFieldNumber) { 933 throw new IllegalArgumentException("Requested field id (" + getFieldIdString(fieldId) 934 + ") does not match current field number (0x" + Integer.toHexString( 935 mFieldNumber) 936 + ") at offset 0x" + Integer.toHexString(getOffset()) 937 + dumpDebugData()); 938 } 939 } 940 941 942 /** 943 * Check a wire type against current wire type. 944 * 945 * @param wireType - throws if wireType does not match mWireType. 946 */ assertWireType(int wireType)947 private void assertWireType(int wireType) { 948 if (wireType != mWireType) { 949 throw new WireTypeMismatchException( 950 "Current wire type " + getWireTypeString(mWireType) 951 + " does not match expected wire type " + getWireTypeString(wireType) 952 + " at offset 0x" + Integer.toHexString(getOffset()) 953 + dumpDebugData()); 954 } 955 } 956 957 /** 958 * Check if there is data ready to be read. 959 */ assertFreshData()960 private void assertFreshData() { 961 if ((mState & STATE_STARTED_FIELD_READ) != STATE_STARTED_FIELD_READ) { 962 throw new ProtoParseException( 963 "Attempting to read already read field at offset 0x" + Integer.toHexString( 964 getOffset()) + dumpDebugData()); 965 } 966 } 967 968 /** 969 * Dump debugging data about the buffer. 970 */ dumpDebugData()971 public String dumpDebugData() { 972 StringBuilder sb = new StringBuilder(); 973 974 sb.append("\nmFieldNumber : 0x").append(Integer.toHexString(mFieldNumber)); 975 sb.append("\nmWireType : 0x").append(Integer.toHexString(mWireType)); 976 sb.append("\nmState : 0x").append(Integer.toHexString(mState)); 977 sb.append("\nmDiscardedBytes : 0x").append(Integer.toHexString(mDiscardedBytes)); 978 sb.append("\nmOffset : 0x").append(Integer.toHexString(mOffset)); 979 sb.append("\nmExpectedObjectTokenStack : ") 980 .append(Objects.toString(mExpectedObjectTokenStack)); 981 sb.append("\nmDepth : 0x").append(Integer.toHexString(mDepth)); 982 sb.append("\nmBuffer : ").append(Arrays.toString(mBuffer)); 983 sb.append("\nmBufferSize : 0x").append(Integer.toHexString(mBufferSize)); 984 sb.append("\nmEnd : 0x").append(Integer.toHexString(mEnd)); 985 986 return sb.toString(); 987 } 988 } 989