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