package com.google.android.exoplayer.extractor.webm;
import com.google.android.exoplayer.extractor.ExtractorInput;
import java.io.EOFException;
import java.io.IOException;
/**
* Reads EBML variable-length integers (varints) from an {@link ExtractorInput}.
*/
/* package */ final class VarintReader {
private static final int STATE_BEGIN_READING = 0;
private static final int STATE_READ_CONTENTS = 1;
/**
* The first byte of a variable-length integer (varint) will have one of these bit masks
* indicating the total length in bytes.
*
* <p>{@code 0x80} is a one-byte integer, {@code 0x40} is two bytes, and so on up to eight bytes.
*/
private static final int[] VARINT_LENGTH_MASKS = new int[] {
0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
};
private final byte[] scratch;
private int state;
private int length;
public VarintReader() {
scratch = new byte[8];
}
/**
* Resets the reader to start reading a new variable-length integer.
*/
public void reset() {
state = STATE_BEGIN_READING;
length = 0;
}
/**
* Reads an EBML variable-length integer (varint) from an {@link ExtractorInput} such that
* reading can be resumed later if an error occurs having read only some of it.
* <p>
* If an value is successfully read, then the reader will automatically reset itself ready to
* read another value.
* <p>
* If an {@link IOException} or {@link InterruptedException} is throw, the read can be resumed
* later by calling this method again, passing an {@link ExtractorInput} providing data starting
* where the previous one left off.
*
* @param input The {@link ExtractorInput} from which the integer should be read.
* @param allowEndOfInput True if encountering the end of the input having read no data is
* allowed, and should result in {@code -1} being returned. False if it should be
* considered an error, causing an {@link EOFException} to be thrown.
* @param removeLengthMask Removes the variable-length integer length mask from the value
* @return The read value, or -1 if {@code allowEndOfStream} is true and the end of the input was
* encountered.
* @throws IOException If an error occurs reading from the input.
* @throws InterruptedException If the thread is interrupted.
*/
public long readUnsignedVarint(ExtractorInput input, boolean allowEndOfInput,
boolean removeLengthMask) throws IOException, InterruptedException {
if (state == STATE_BEGIN_READING) {
// Read the first byte to establish the length.
if (!input.readFully(scratch, 0, 1, allowEndOfInput)) {
return -1;
}
int firstByte = scratch[0] & 0xFF;
length = -1;
for (int i = 0; i < VARINT_LENGTH_MASKS.length; i++) {
if ((VARINT_LENGTH_MASKS[i] & firstByte) != 0) {
length = i + 1;
break;
}
}
if (length == -1) {
throw new IllegalStateException("No valid varint length mask found");
}
state = STATE_READ_CONTENTS;
}
// Read the remaining bytes.
input.readFully(scratch, 1, length - 1);
// Parse the value.
if (removeLengthMask) {
scratch[0] &= ~VARINT_LENGTH_MASKS[length - 1];
}
long varint = 0;
for (int i = 0; i < length; i++) {
varint = (varint << 8) | (scratch[i] & 0xFF);
}
state = STATE_BEGIN_READING;
return varint;
}
/**
* Returns the number of bytes occupied by the most recently parsed varint.
*/
public int getLastLength() {
return length;
}
}