• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      * Attempt to guess the next field. If there is a match, the field data will be ready to read.
257      * If there is no match, nextField will need to be called to get the field number
258      *
259      * @return true if fieldId matches the next field, false if not
260      */
isNextField(long fieldId)261     public boolean isNextField(long fieldId) throws IOException {
262         if (nextField() == (int) fieldId) {
263             return true;
264         }
265         // Note to reuse the info from the nextField call in the next call.
266         mState |= STATE_FIELD_MISS;
267         return false;
268     }
269 
270     /**
271      * Read a single double.
272      * Will throw if the current wire type is not fixed64
273      *
274      * @param fieldId - must match the current field number and field type
275      */
readDouble(long fieldId)276     public double readDouble(long fieldId) throws IOException {
277         assertFreshData();
278         assertFieldNumber(fieldId);
279         checkPacked(fieldId);
280 
281         double value;
282         switch ((int) ((fieldId & FIELD_TYPE_MASK)
283                 >>> FIELD_TYPE_SHIFT)) {
284             case (int) (FIELD_TYPE_DOUBLE >>> FIELD_TYPE_SHIFT):
285                 assertWireType(WIRE_TYPE_FIXED64);
286                 value = Double.longBitsToDouble(readFixed64());
287                 break;
288             default:
289                 throw new IllegalArgumentException(
290                         "Requested field id (" + getFieldIdString(fieldId)
291                                 + ") cannot be read as a double"
292                                 + dumpDebugData());
293         }
294         // Successfully read the field
295         mState &= ~STATE_STARTED_FIELD_READ;
296         return value;
297     }
298 
299     /**
300      * Read a single float.
301      * Will throw if the current wire type is not fixed32
302      *
303      * @param fieldId - must match the current field number and field type
304      */
readFloat(long fieldId)305     public float readFloat(long fieldId) throws IOException {
306         assertFreshData();
307         assertFieldNumber(fieldId);
308         checkPacked(fieldId);
309 
310         float value;
311         switch ((int) ((fieldId & FIELD_TYPE_MASK)
312                 >>> FIELD_TYPE_SHIFT)) {
313             case (int) (FIELD_TYPE_FLOAT >>> FIELD_TYPE_SHIFT):
314                 assertWireType(WIRE_TYPE_FIXED32);
315                 value = Float.intBitsToFloat(readFixed32());
316                 break;
317             default:
318                 throw new IllegalArgumentException(
319                         "Requested field id (" + getFieldIdString(fieldId) + ") is not a float"
320                                 + dumpDebugData());
321         }
322         // Successfully read the field
323         mState &= ~STATE_STARTED_FIELD_READ;
324         return value;
325     }
326 
327     /**
328      * Read a single 32bit or varint proto type field as an int.
329      * Will throw if the current wire type is not varint or fixed32
330      *
331      * @param fieldId - must match the current field number and field type
332      */
readInt(long fieldId)333     public int readInt(long fieldId) throws IOException {
334         assertFreshData();
335         assertFieldNumber(fieldId);
336         checkPacked(fieldId);
337 
338         int value;
339         switch ((int) ((fieldId & FIELD_TYPE_MASK)
340                 >>> FIELD_TYPE_SHIFT)) {
341             case (int) (FIELD_TYPE_FIXED32 >>> FIELD_TYPE_SHIFT):
342             case (int) (FIELD_TYPE_SFIXED32 >>> FIELD_TYPE_SHIFT):
343                 assertWireType(WIRE_TYPE_FIXED32);
344                 value = readFixed32();
345                 break;
346             case (int) (FIELD_TYPE_SINT32 >>> FIELD_TYPE_SHIFT):
347                 assertWireType(WIRE_TYPE_VARINT);
348                 value = decodeZigZag32((int) readVarint());
349                 break;
350             case (int) (FIELD_TYPE_INT32 >>> FIELD_TYPE_SHIFT):
351             case (int) (FIELD_TYPE_UINT32 >>> FIELD_TYPE_SHIFT):
352             case (int) (FIELD_TYPE_ENUM >>> FIELD_TYPE_SHIFT):
353                 assertWireType(WIRE_TYPE_VARINT);
354                 value = (int) readVarint();
355                 break;
356             default:
357                 throw new IllegalArgumentException(
358                         "Requested field id (" + getFieldIdString(fieldId) + ") is not an int"
359                                 + dumpDebugData());
360         }
361         // Successfully read the field
362         mState &= ~STATE_STARTED_FIELD_READ;
363         return value;
364     }
365 
366     /**
367      * Read a single 64bit or varint proto type field as an long.
368      *
369      * @param fieldId - must match the current field number
370      */
readLong(long fieldId)371     public long readLong(long fieldId) throws IOException {
372         assertFreshData();
373         assertFieldNumber(fieldId);
374         checkPacked(fieldId);
375 
376         long value;
377         switch ((int) ((fieldId & FIELD_TYPE_MASK)
378                 >>> FIELD_TYPE_SHIFT)) {
379             case (int) (FIELD_TYPE_FIXED64 >>> FIELD_TYPE_SHIFT):
380             case (int) (FIELD_TYPE_SFIXED64 >>> FIELD_TYPE_SHIFT):
381                 assertWireType(WIRE_TYPE_FIXED64);
382                 value = readFixed64();
383                 break;
384             case (int) (FIELD_TYPE_SINT64 >>> FIELD_TYPE_SHIFT):
385                 assertWireType(WIRE_TYPE_VARINT);
386                 value = decodeZigZag64(readVarint());
387                 break;
388             case (int) (FIELD_TYPE_INT64 >>> FIELD_TYPE_SHIFT):
389             case (int) (FIELD_TYPE_UINT64 >>> FIELD_TYPE_SHIFT):
390                 assertWireType(WIRE_TYPE_VARINT);
391                 value = readVarint();
392                 break;
393             default:
394                 throw new IllegalArgumentException(
395                         "Requested field id (" + getFieldIdString(fieldId) + ") is not an long"
396                                 + dumpDebugData());
397         }
398         // Successfully read the field
399         mState &= ~STATE_STARTED_FIELD_READ;
400         return value;
401     }
402 
403     /**
404      * Read a single 32bit or varint proto type field as an boolean.
405      *
406      * @param fieldId - must match the current field number
407      */
readBoolean(long fieldId)408     public boolean readBoolean(long fieldId) throws IOException {
409         assertFreshData();
410         assertFieldNumber(fieldId);
411         checkPacked(fieldId);
412 
413         boolean value;
414         switch ((int) ((fieldId & FIELD_TYPE_MASK)
415                 >>> FIELD_TYPE_SHIFT)) {
416             case (int) (FIELD_TYPE_BOOL >>> FIELD_TYPE_SHIFT):
417                 assertWireType(WIRE_TYPE_VARINT);
418                 value = readVarint() != 0;
419                 break;
420             default:
421                 throw new IllegalArgumentException(
422                         "Requested field id (" + getFieldIdString(fieldId) + ") is not an boolean"
423                                 + dumpDebugData());
424         }
425         // Successfully read the field
426         mState &= ~STATE_STARTED_FIELD_READ;
427         return value;
428     }
429 
430     /**
431      * Read a string field
432      *
433      * @param fieldId - must match the current field number
434      */
readString(long fieldId)435     public String readString(long fieldId) throws IOException {
436         assertFreshData();
437         assertFieldNumber(fieldId);
438 
439         String value;
440         switch ((int) ((fieldId & FIELD_TYPE_MASK) >>> FIELD_TYPE_SHIFT)) {
441             case (int) (FIELD_TYPE_STRING >>> FIELD_TYPE_SHIFT):
442                 assertWireType(WIRE_TYPE_LENGTH_DELIMITED);
443                 int len = (int) readVarint();
444                 value = readRawString(len);
445                 break;
446             default:
447                 throw new IllegalArgumentException(
448                         "Requested field id(" + getFieldIdString(fieldId)
449                                 + ") is not an string"
450                                 + dumpDebugData());
451         }
452         // Successfully read the field
453         mState &= ~STATE_STARTED_FIELD_READ;
454         return value;
455     }
456 
457     /**
458      * Read a bytes field
459      *
460      * @param fieldId - must match the current field number
461      */
readBytes(long fieldId)462     public byte[] readBytes(long fieldId) throws IOException {
463         assertFreshData();
464         assertFieldNumber(fieldId);
465 
466         byte[] value;
467         switch ((int) ((fieldId & FIELD_TYPE_MASK) >>> FIELD_TYPE_SHIFT)) {
468             case (int) (FIELD_TYPE_MESSAGE >>> FIELD_TYPE_SHIFT):
469             case (int) (FIELD_TYPE_BYTES >>> FIELD_TYPE_SHIFT):
470                 assertWireType(WIRE_TYPE_LENGTH_DELIMITED);
471                 int len = (int) readVarint();
472                 value = readRawBytes(len);
473                 break;
474             default:
475                 throw new IllegalArgumentException(
476                         "Requested field type (" + getFieldIdString(fieldId)
477                                 + ") cannot be read as raw bytes"
478                                 + dumpDebugData());
479         }
480         // Successfully read the field
481         mState &= ~STATE_STARTED_FIELD_READ;
482         return value;
483     }
484 
485     /**
486      * Start the read of an embedded Object
487      *
488      * @param fieldId - must match the current field number
489      * @return a token. The token must be handed back when finished reading embedded Object
490      */
start(long fieldId)491     public long start(long fieldId) throws IOException {
492         assertFreshData();
493         assertFieldNumber(fieldId);
494         assertWireType(WIRE_TYPE_LENGTH_DELIMITED);
495 
496         int messageSize = (int) readVarint();
497 
498         if (mExpectedObjectTokenStack == null) {
499             mExpectedObjectTokenStack = new ArrayList<>();
500         }
501         if (++mDepth == mExpectedObjectTokenStack.size()) {
502             // Create a token to keep track of nested Object and extend the object stack
503             mExpectedObjectTokenStack.add(makeToken(0,
504                     (fieldId & FIELD_COUNT_REPEATED) == FIELD_COUNT_REPEATED, mDepth,
505                     (int) fieldId, getOffset() + messageSize));
506 
507         } else {
508             // Create a token to keep track of nested Object
509             mExpectedObjectTokenStack.set(mDepth, makeToken(0,
510                     (fieldId & FIELD_COUNT_REPEATED) == FIELD_COUNT_REPEATED, mDepth,
511                     (int) fieldId, getOffset() + messageSize));
512         }
513 
514         // Sanity check
515         if (mDepth > 0
516                 && getOffsetFromToken(mExpectedObjectTokenStack.get(mDepth))
517                 > getOffsetFromToken(mExpectedObjectTokenStack.get(mDepth - 1))) {
518             throw new ProtoParseException("Embedded Object ("
519                     + token2String(mExpectedObjectTokenStack.get(mDepth))
520                     + ") ends after of parent Objects's ("
521                     + token2String(mExpectedObjectTokenStack.get(mDepth - 1))
522                     + ") end"
523                     + dumpDebugData());
524         }
525         mState &= ~STATE_STARTED_FIELD_READ;
526         return mExpectedObjectTokenStack.get(mDepth);
527     }
528 
529     /**
530      * Note the end of a nested object. Must be called to continue streaming the rest of the proto.
531      * end can be called mid object parse. The offset will be moved to the next field outside the
532      * object.
533      *
534      * @param token - token
535      */
end(long token)536     public void end(long token) {
537         // Sanity check to make sure user is keeping track of their embedded messages
538         if (mExpectedObjectTokenStack.get(mDepth) != token) {
539             throw new ProtoParseException(
540                     "end token " + token + " does not match current message token "
541                             + mExpectedObjectTokenStack.get(mDepth)
542                             + dumpDebugData());
543         }
544         if (getOffsetFromToken(mExpectedObjectTokenStack.get(mDepth)) > getOffset()) {
545             // Did not read all of the message, skip to the end
546             incOffset(getOffsetFromToken(mExpectedObjectTokenStack.get(mDepth)) - getOffset());
547         }
548         mDepth--;
549         mState &= ~STATE_STARTED_FIELD_READ;
550     }
551 
552     /**
553      * Read the tag at the start of the next field and collect field number and wire type.
554      * Will set mFieldNumber to NO_MORE_FIELDS if end of buffer/stream reached.
555      */
readTag()556     private void readTag() throws IOException {
557         fillBuffer();
558         if (mOffset >= mEnd) {
559             // reached end of the stream
560             mFieldNumber = NO_MORE_FIELDS;
561             return;
562         }
563         int tag = (int) readVarint();
564         mFieldNumber = tag >>> FIELD_ID_SHIFT;
565         mWireType = tag & WIRE_TYPE_MASK;
566         mState |= STATE_STARTED_FIELD_READ;
567     }
568 
569     /**
570      * Decode a 32 bit ZigZag encoded signed int.
571      *
572      * @param n - int to decode
573      * @return the decoded signed int
574      */
decodeZigZag32(final int n)575     public int decodeZigZag32(final int n) {
576         return (n >>> 1) ^ -(n & 1);
577     }
578 
579     /**
580      * Decode a 64 bit ZigZag encoded signed long.
581      *
582      * @param n - long to decode
583      * @return the decoded signed long
584      */
decodeZigZag64(final long n)585     public long decodeZigZag64(final long n) {
586         return (n >>> 1) ^ -(n & 1);
587     }
588 
589     /**
590      * Read a varint from the buffer
591      *
592      * @return the varint as a long
593      */
readVarint()594     private long readVarint() throws IOException {
595         long value = 0;
596         int shift = 0;
597         while (true) {
598             fillBuffer();
599             // Limit how much bookkeeping is done by checking how far away the end of the buffer is
600             // and directly accessing buffer up until the end.
601             final int fragment = mEnd - mOffset;
602             for (int i = 0; i < fragment; i++) {
603                 byte b = mBuffer[(mOffset + i)];
604                 value |= (b & 0x7FL) << shift;
605                 if ((b & 0x80) == 0) {
606                     incOffset(i + 1);
607                     return value;
608                 }
609                 shift += 7;
610                 if (shift > 63) {
611                     throw new ProtoParseException(
612                             "Varint is too large at offset 0x"
613                                     + Integer.toHexString(getOffset() + i)
614                                     + dumpDebugData());
615                 }
616             }
617             // Hit the end of the buffer, do some incrementing and checking, then continue
618             incOffset(fragment);
619         }
620     }
621 
622     /**
623      * Read a fixed 32 bit int from the buffer
624      *
625      * @return the fixed32 as a int
626      */
readFixed32()627     private int readFixed32() throws IOException {
628         // check for fast path, which is likely with a reasonable buffer size
629         if (mOffset + 4 <= mEnd) {
630             // don't bother filling buffer since we know the end is plenty far away
631             incOffset(4);
632             return (mBuffer[mOffset - 4] & 0xFF)
633                     | ((mBuffer[mOffset - 3] & 0xFF) << 8)
634                     | ((mBuffer[mOffset - 2] & 0xFF) << 16)
635                     | ((mBuffer[mOffset - 1] & 0xFF) << 24);
636         }
637 
638         // the Fixed32 crosses the edge of a chunk, read the Fixed32 in multiple fragments.
639         // There will be two fragment reads except when the chunk size is 2 or less.
640         int value = 0;
641         int shift = 0;
642         int bytesLeft = 4;
643         while (bytesLeft > 0) {
644             fillBuffer();
645             // Find the number of bytes available until the end of the chunk or Fixed32
646             int fragment = (mEnd - mOffset) < bytesLeft ? (mEnd - mOffset) : bytesLeft;
647             incOffset(fragment);
648             bytesLeft -= fragment;
649             while (fragment > 0) {
650                 value |= ((mBuffer[mOffset - fragment] & 0xFF) << shift);
651                 fragment--;
652                 shift += 8;
653             }
654         }
655         return value;
656     }
657 
658     /**
659      * Read a fixed 64 bit long from the buffer
660      *
661      * @return the fixed64 as a long
662      */
readFixed64()663     private long readFixed64() throws IOException {
664         // check for fast path, which is likely with a reasonable buffer size
665         if (mOffset + 8 <= mEnd) {
666             // don't bother filling buffer since we know the end is plenty far away
667             incOffset(8);
668             return (mBuffer[mOffset - 8] & 0xFFL)
669                     | ((mBuffer[mOffset - 7] & 0xFFL) << 8)
670                     | ((mBuffer[mOffset - 6] & 0xFFL) << 16)
671                     | ((mBuffer[mOffset - 5] & 0xFFL) << 24)
672                     | ((mBuffer[mOffset - 4] & 0xFFL) << 32)
673                     | ((mBuffer[mOffset - 3] & 0xFFL) << 40)
674                     | ((mBuffer[mOffset - 2] & 0xFFL) << 48)
675                     | ((mBuffer[mOffset - 1] & 0xFFL) << 56);
676         }
677 
678         // the Fixed64 crosses the edge of a chunk, read the Fixed64 in multiple fragments.
679         // There will be two fragment reads except when the chunk size is 6 or less.
680         long value = 0;
681         int shift = 0;
682         int bytesLeft = 8;
683         while (bytesLeft > 0) {
684             fillBuffer();
685             // Find the number of bytes available until the end of the chunk or Fixed64
686             int fragment = (mEnd - mOffset) < bytesLeft ? (mEnd - mOffset) : bytesLeft;
687             incOffset(fragment);
688             bytesLeft -= fragment;
689             while (fragment > 0) {
690                 value |= ((mBuffer[(mOffset - fragment)] & 0xFFL) << shift);
691                 fragment--;
692                 shift += 8;
693             }
694         }
695         return value;
696     }
697 
698     /**
699      * Read raw bytes from the buffer
700      *
701      * @param n - number of bytes to read
702      * @return a byte array with raw bytes
703      */
readRawBytes(int n)704     private byte[] readRawBytes(int n) throws IOException {
705         byte[] buffer = new byte[n];
706         int pos = 0;
707         while (mOffset + n - pos > mEnd) {
708             int fragment = mEnd - mOffset;
709             if (fragment > 0) {
710                 System.arraycopy(mBuffer, mOffset, buffer, pos, fragment);
711                 incOffset(fragment);
712                 pos += fragment;
713             }
714             fillBuffer();
715             if (mOffset >= mEnd) {
716                 throw new ProtoParseException(
717                         "Unexpectedly reached end of the InputStream at offset 0x"
718                                 + Integer.toHexString(mEnd)
719                                 + dumpDebugData());
720             }
721         }
722         System.arraycopy(mBuffer, mOffset, buffer, pos, n - pos);
723         incOffset(n - pos);
724         return buffer;
725     }
726 
727     /**
728      * Read raw string from the buffer
729      *
730      * @param n - number of bytes to read
731      * @return a string
732      */
readRawString(int n)733     private String readRawString(int n) throws IOException {
734         fillBuffer();
735         if (mOffset + n <= mEnd) {
736             // fast path read. String is well within the current buffer
737             String value = new String(mBuffer, mOffset, n, StandardCharsets.UTF_8);
738             incOffset(n);
739             return value;
740         } else if (n <= mBufferSize) {
741             // String extends past buffer, but can be encapsulated in a buffer. Copy the first chunk
742             // of the string to the start of the buffer and then fill the rest of the buffer from
743             // the stream.
744             final int stringHead = mEnd - mOffset;
745             System.arraycopy(mBuffer, mOffset, mBuffer, 0, stringHead);
746             mEnd = stringHead + mStream.read(mBuffer, stringHead, n - stringHead);
747 
748             mDiscardedBytes += mOffset;
749             mOffset = 0;
750 
751             String value = new String(mBuffer, mOffset, n, StandardCharsets.UTF_8);
752             incOffset(n);
753             return value;
754         }
755         // Otherwise, the string is too large to use the buffer. Create the string from a
756         // separate byte array.
757         return new String(readRawBytes(n), 0, n, StandardCharsets.UTF_8);
758     }
759 
760     /**
761      * Fill the buffer with a chunk from the stream if need be.
762      * Will skip chunks until mOffset is reached
763      */
fillBuffer()764     private void fillBuffer() throws IOException {
765         if (mOffset >= mEnd && mStream != null) {
766             mOffset -= mEnd;
767             mDiscardedBytes += mEnd;
768             if (mOffset >= mBufferSize) {
769                 int skipped = (int) mStream.skip((mOffset / mBufferSize) * mBufferSize);
770                 mDiscardedBytes += skipped;
771                 mOffset -= skipped;
772             }
773             mEnd = mStream.read(mBuffer);
774         }
775     }
776 
777     /**
778      * Skips the rest of current field and moves to the start of the next field. This should only be
779      * called while state is STATE_STARTED_FIELD_READ
780      */
skip()781     public void skip() throws IOException {
782         if ((mState & STATE_READING_PACKED) == STATE_READING_PACKED) {
783             incOffset(mPackedEnd - getOffset());
784         } else {
785             switch (mWireType) {
786                 case WIRE_TYPE_VARINT:
787                     byte b;
788                     do {
789                         fillBuffer();
790                         b = mBuffer[mOffset];
791                         incOffset(1);
792                     } while ((b & 0x80) != 0);
793                     break;
794                 case WIRE_TYPE_FIXED64:
795                     incOffset(8);
796                     break;
797                 case WIRE_TYPE_LENGTH_DELIMITED:
798                     fillBuffer();
799                     int length = (int) readVarint();
800                     incOffset(length);
801                     break;
802                 /*
803             case WIRE_TYPE_START_GROUP:
804                 // Not implemented
805                 break;
806             case WIRE_TYPE_END_GROUP:
807                 // Not implemented
808                 break;
809                 */
810                 case WIRE_TYPE_FIXED32:
811                     incOffset(4);
812                     break;
813                 default:
814                     throw new ProtoParseException(
815                             "Unexpected wire type: " + mWireType + " at offset 0x"
816                                     + Integer.toHexString(mOffset)
817                                     + dumpDebugData());
818             }
819         }
820         mState &= ~STATE_STARTED_FIELD_READ;
821     }
822 
823     /**
824      * Increment the offset and handle all the relevant bookkeeping
825      * Refilling the buffer when its end is reached will be handled elsewhere (ideally just before
826      * a read, to avoid unnecessary reads from stream)
827      *
828      * @param n - number of bytes to increment
829      */
incOffset(int n)830     private void incOffset(int n) {
831         mOffset += n;
832 
833         if (mDepth >= 0 && getOffset() > getOffsetFromToken(
834                 mExpectedObjectTokenStack.get(mDepth))) {
835             throw new ProtoParseException("Unexpectedly reached end of embedded object.  "
836                     + token2String(mExpectedObjectTokenStack.get(mDepth))
837                     + dumpDebugData());
838         }
839     }
840 
841     /**
842      * Check the current wire type to determine if current numeric field is packed. If it is packed,
843      * set up to deal with the field
844      * This should only be called for primitive numeric field types.
845      *
846      * @param fieldId - used to determine what the packed wire type is.
847      */
checkPacked(long fieldId)848     private void checkPacked(long fieldId) throws IOException {
849         if (mWireType == WIRE_TYPE_LENGTH_DELIMITED) {
850             // Primitive Field is length delimited, must be a packed field.
851             final int length = (int) readVarint();
852             mPackedEnd = getOffset() + length;
853             mState |= STATE_READING_PACKED;
854 
855             // Fake the wire type, based on the field type
856             switch ((int) ((fieldId & FIELD_TYPE_MASK)
857                     >>> FIELD_TYPE_SHIFT)) {
858                 case (int) (FIELD_TYPE_FLOAT >>> FIELD_TYPE_SHIFT):
859                 case (int) (FIELD_TYPE_FIXED32 >>> FIELD_TYPE_SHIFT):
860                 case (int) (FIELD_TYPE_SFIXED32 >>> FIELD_TYPE_SHIFT):
861                     if (length % 4 != 0) {
862                         throw new IllegalArgumentException(
863                                 "Requested field id (" + getFieldIdString(fieldId)
864                                         + ") packed length " + length
865                                         + " is not aligned for fixed32"
866                                         + dumpDebugData());
867                     }
868                     mWireType = WIRE_TYPE_FIXED32;
869                     break;
870                 case (int) (FIELD_TYPE_DOUBLE >>> FIELD_TYPE_SHIFT):
871                 case (int) (FIELD_TYPE_FIXED64 >>> FIELD_TYPE_SHIFT):
872                 case (int) (FIELD_TYPE_SFIXED64 >>> FIELD_TYPE_SHIFT):
873                     if (length % 8 != 0) {
874                         throw new IllegalArgumentException(
875                                 "Requested field id (" + getFieldIdString(fieldId)
876                                         + ") packed length " + length
877                                         + " is not aligned for fixed64"
878                                         + dumpDebugData());
879                     }
880                     mWireType = WIRE_TYPE_FIXED64;
881                     break;
882                 case (int) (FIELD_TYPE_SINT32 >>> FIELD_TYPE_SHIFT):
883                 case (int) (FIELD_TYPE_INT32 >>> FIELD_TYPE_SHIFT):
884                 case (int) (FIELD_TYPE_UINT32 >>> FIELD_TYPE_SHIFT):
885                 case (int) (FIELD_TYPE_SINT64 >>> FIELD_TYPE_SHIFT):
886                 case (int) (FIELD_TYPE_INT64 >>> FIELD_TYPE_SHIFT):
887                 case (int) (FIELD_TYPE_UINT64 >>> FIELD_TYPE_SHIFT):
888                 case (int) (FIELD_TYPE_ENUM >>> FIELD_TYPE_SHIFT):
889                 case (int) (FIELD_TYPE_BOOL >>> FIELD_TYPE_SHIFT):
890                     mWireType = WIRE_TYPE_VARINT;
891                     break;
892                 default:
893                     throw new IllegalArgumentException(
894                             "Requested field id (" + getFieldIdString(fieldId)
895                                     + ") is not a packable field"
896                                     + dumpDebugData());
897             }
898         }
899     }
900 
901 
902     /**
903      * Check a field id constant against current field number
904      *
905      * @param fieldId - throws if fieldId does not match mFieldNumber
906      */
assertFieldNumber(long fieldId)907     private void assertFieldNumber(long fieldId) {
908         if ((int) fieldId != mFieldNumber) {
909             throw new IllegalArgumentException("Requested field id (" + getFieldIdString(fieldId)
910                     + ") does not match current field number (0x" + Integer.toHexString(
911                     mFieldNumber)
912                     + ") at offset 0x" + Integer.toHexString(getOffset())
913                     + dumpDebugData());
914         }
915     }
916 
917 
918     /**
919      * Check a wire type against current wire type.
920      *
921      * @param wireType - throws if wireType does not match mWireType.
922      */
assertWireType(int wireType)923     private void assertWireType(int wireType) {
924         if (wireType != mWireType) {
925             throw new WireTypeMismatchException(
926                     "Current wire type " + getWireTypeString(mWireType)
927                             + " does not match expected wire type " + getWireTypeString(wireType)
928                             + " at offset 0x" + Integer.toHexString(getOffset())
929                             + dumpDebugData());
930         }
931     }
932 
933     /**
934      * Check if there is data ready to be read.
935      */
assertFreshData()936     private void assertFreshData() {
937         if ((mState & STATE_STARTED_FIELD_READ) != STATE_STARTED_FIELD_READ) {
938             throw new ProtoParseException(
939                     "Attempting to read already read field at offset 0x" + Integer.toHexString(
940                             getOffset()) + dumpDebugData());
941         }
942     }
943 
944     /**
945      * Dump debugging data about the buffer.
946      */
dumpDebugData()947     public String dumpDebugData() {
948         StringBuilder sb = new StringBuilder();
949 
950         sb.append("\nmFieldNumber : 0x" + Integer.toHexString(mFieldNumber));
951         sb.append("\nmWireType : 0x" + Integer.toHexString(mWireType));
952         sb.append("\nmState : 0x" + Integer.toHexString(mState));
953         sb.append("\nmDiscardedBytes : 0x" + Integer.toHexString(mDiscardedBytes));
954         sb.append("\nmOffset : 0x" + Integer.toHexString(mOffset));
955         sb.append("\nmExpectedObjectTokenStack : ");
956         if (mExpectedObjectTokenStack == null) {
957             sb.append("null");
958         } else {
959             sb.append(mExpectedObjectTokenStack);
960         }
961         sb.append("\nmDepth : 0x" + Integer.toHexString(mDepth));
962         sb.append("\nmBuffer : ");
963         if (mBuffer == null) {
964             sb.append("null");
965         } else {
966             sb.append(mBuffer);
967         }
968         sb.append("\nmBufferSize : 0x" + Integer.toHexString(mBufferSize));
969         sb.append("\nmEnd : 0x" + Integer.toHexString(mEnd));
970 
971         return sb.toString();
972     }
973 }
974