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