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