diff options
Diffstat (limited to 'mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/extractor/mkv/VarintReader.java')
-rw-r--r-- | mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/extractor/mkv/VarintReader.java | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/extractor/mkv/VarintReader.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/extractor/mkv/VarintReader.java new file mode 100644 index 0000000000..8a8d572ea5 --- /dev/null +++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/extractor/mkv/VarintReader.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mozilla.thirdparty.com.google.android.exoplayer2.extractor.mkv; + +import org.mozilla.thirdparty.com.google.android.exoplayer2.C; +import org.mozilla.thirdparty.com.google.android.exoplayer2.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 long[] VARINT_LENGTH_MASKS = new long[] { + 0x80L, 0x40L, 0x20L, 0x10L, 0x08L, 0x04L, 0x02L, 0x01L + }; + + 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 {@link C#RESULT_END_OF_INPUT} 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. + * @param maximumAllowedLength Maximum allowed length of the variable integer to be read. + * @return The read value, or {@link C#RESULT_END_OF_INPUT} if {@code allowEndOfStream} is true + * and the end of the input was encountered, or {@link C#RESULT_MAX_LENGTH_EXCEEDED} if the + * length of the varint exceeded maximumAllowedLength. + * @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, int maximumAllowedLength) throws IOException, InterruptedException { + if (state == STATE_BEGIN_READING) { + // Read the first byte to establish the length. + if (!input.readFully(scratch, 0, 1, allowEndOfInput)) { + return C.RESULT_END_OF_INPUT; + } + int firstByte = scratch[0] & 0xFF; + length = parseUnsignedVarintLength(firstByte); + if (length == C.LENGTH_UNSET) { + throw new IllegalStateException("No valid varint length mask found"); + } + state = STATE_READ_CONTENTS; + } + + if (length > maximumAllowedLength) { + state = STATE_BEGIN_READING; + return C.RESULT_MAX_LENGTH_EXCEEDED; + } + + if (length != 1) { + // Read the remaining bytes. + input.readFully(scratch, 1, length - 1); + } + + state = STATE_BEGIN_READING; + return assembleVarint(scratch, length, removeLengthMask); + } + + /** + * Returns the number of bytes occupied by the most recently parsed varint. + */ + public int getLastLength() { + return length; + } + + /** + * Parses and the length of the varint given the first byte. + * + * @param firstByte First byte of the varint. + * @return Length of the varint beginning with the given byte if it was valid, + * {@link C#LENGTH_UNSET} otherwise. + */ + public static int parseUnsignedVarintLength(int firstByte) { + int varIntLength = C.LENGTH_UNSET; + for (int i = 0; i < VARINT_LENGTH_MASKS.length; i++) { + if ((VARINT_LENGTH_MASKS[i] & firstByte) != 0) { + varIntLength = i + 1; + break; + } + } + return varIntLength; + } + + /** + * Assemble a varint from the given byte array. + * + * @param varintBytes Bytes that make up the varint. + * @param varintLength Length of the varint to assemble. + * @param removeLengthMask Removes the variable-length integer length mask from the value. + * @return Parsed and assembled varint. + */ + public static long assembleVarint(byte[] varintBytes, int varintLength, + boolean removeLengthMask) { + long varint = varintBytes[0] & 0xFFL; + if (removeLengthMask) { + varint &= ~VARINT_LENGTH_MASKS[varintLength - 1]; + } + for (int i = 1; i < varintLength; i++) { + varint = (varint << 8) | (varintBytes[i] & 0xFFL); + } + return varint; + } + +} |