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