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