• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 package com.google.android.exoplayer2.extractor.mkv;
17 
18 import com.google.android.exoplayer2.C;
19 import com.google.android.exoplayer2.extractor.ExtractorInput;
20 import java.io.EOFException;
21 import java.io.IOException;
22 
23 /**
24  * Reads EBML variable-length integers (varints) from an {@link ExtractorInput}.
25  */
26 /* package */ final class VarintReader {
27 
28   private static final int STATE_BEGIN_READING = 0;
29   private static final int STATE_READ_CONTENTS = 1;
30 
31   /**
32    * The first byte of a variable-length integer (varint) will have one of these bit masks
33    * indicating the total length in bytes.
34    *
35    * <p>{@code 0x80} is a one-byte integer, {@code 0x40} is two bytes, and so on up to eight bytes.
36    */
37   private static final long[] VARINT_LENGTH_MASKS = new long[] {
38     0x80L, 0x40L, 0x20L, 0x10L, 0x08L, 0x04L, 0x02L, 0x01L
39   };
40 
41   private final byte[] scratch;
42 
43   private int state;
44   private int length;
45 
VarintReader()46   public VarintReader() {
47     scratch = new byte[8];
48   }
49 
50   /**
51    * Resets the reader to start reading a new variable-length integer.
52    */
reset()53   public void reset() {
54     state = STATE_BEGIN_READING;
55     length = 0;
56   }
57 
58   /**
59    * Reads an EBML variable-length integer (varint) from an {@link ExtractorInput} such that reading
60    * can be resumed later if an error occurs having read only some of it.
61    *
62    * <p>If an value is successfully read, then the reader will automatically reset itself ready to
63    * read another value.
64    *
65    * <p>If an {@link IOException} is thrown, the read can be resumed later by calling this method
66    * again, passing an {@link ExtractorInput} providing data starting where the previous one left
67    * off.
68    *
69    * @param input The {@link ExtractorInput} from which the integer should be read.
70    * @param allowEndOfInput True if encountering the end of the input having read no data is
71    *     allowed, and should result in {@link C#RESULT_END_OF_INPUT} being returned. False if it
72    *     should be considered an error, causing an {@link EOFException} to be thrown.
73    * @param removeLengthMask Removes the variable-length integer length mask from the value.
74    * @param maximumAllowedLength Maximum allowed length of the variable integer to be read.
75    * @return The read value, or {@link C#RESULT_END_OF_INPUT} if {@code allowEndOfStream} is true
76    *     and the end of the input was encountered, or {@link C#RESULT_MAX_LENGTH_EXCEEDED} if the
77    *     length of the varint exceeded maximumAllowedLength.
78    * @throws IOException If an error occurs reading from the input.
79    */
readUnsignedVarint( ExtractorInput input, boolean allowEndOfInput, boolean removeLengthMask, int maximumAllowedLength)80   public long readUnsignedVarint(
81       ExtractorInput input,
82       boolean allowEndOfInput,
83       boolean removeLengthMask,
84       int maximumAllowedLength)
85       throws IOException {
86     if (state == STATE_BEGIN_READING) {
87       // Read the first byte to establish the length.
88       if (!input.readFully(scratch, 0, 1, allowEndOfInput)) {
89         return C.RESULT_END_OF_INPUT;
90       }
91       int firstByte = scratch[0] & 0xFF;
92       length = parseUnsignedVarintLength(firstByte);
93       if (length == C.LENGTH_UNSET) {
94         throw new IllegalStateException("No valid varint length mask found");
95       }
96       state = STATE_READ_CONTENTS;
97     }
98 
99     if (length > maximumAllowedLength) {
100       state = STATE_BEGIN_READING;
101       return C.RESULT_MAX_LENGTH_EXCEEDED;
102     }
103 
104     if (length != 1) {
105       // Read the remaining bytes.
106       input.readFully(scratch, 1, length - 1);
107     }
108 
109     state = STATE_BEGIN_READING;
110     return assembleVarint(scratch, length, removeLengthMask);
111   }
112 
113   /**
114    * Returns the number of bytes occupied by the most recently parsed varint.
115    */
getLastLength()116   public int getLastLength() {
117     return length;
118   }
119 
120   /**
121    * Parses and the length of the varint given the first byte.
122    *
123    * @param firstByte First byte of the varint.
124    * @return Length of the varint beginning with the given byte if it was valid,
125    *     {@link C#LENGTH_UNSET} otherwise.
126    */
parseUnsignedVarintLength(int firstByte)127   public static int parseUnsignedVarintLength(int firstByte) {
128     int varIntLength = C.LENGTH_UNSET;
129     for (int i = 0; i < VARINT_LENGTH_MASKS.length; i++) {
130       if ((VARINT_LENGTH_MASKS[i] & firstByte) != 0) {
131         varIntLength = i + 1;
132         break;
133       }
134     }
135     return varIntLength;
136   }
137 
138   /**
139    * Assemble a varint from the given byte array.
140    *
141    * @param varintBytes Bytes that make up the varint.
142    * @param varintLength Length of the varint to assemble.
143    * @param removeLengthMask Removes the variable-length integer length mask from the value.
144    * @return Parsed and assembled varint.
145    */
assembleVarint(byte[] varintBytes, int varintLength, boolean removeLengthMask)146   public static long assembleVarint(byte[] varintBytes, int varintLength,
147       boolean removeLengthMask) {
148     long varint = varintBytes[0] & 0xFFL;
149     if (removeLengthMask) {
150       varint &= ~VARINT_LENGTH_MASKS[varintLength - 1];
151     }
152     for (int i = 1; i < varintLength; i++) {
153       varint = (varint << 8) | (varintBytes[i] & 0xFFL);
154     }
155     return varint;
156   }
157 
158 }
159