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