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