| /* |
| * 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 com.google.android.exoplayer2.util; |
| |
| import androidx.annotation.Nullable; |
| import com.google.common.base.Charsets; |
| import java.nio.ByteBuffer; |
| import java.nio.charset.Charset; |
| import java.util.Arrays; |
| |
| /** |
| * Wraps a byte array, providing a set of methods for parsing data from it. Numerical values are |
| * parsed with the assumption that their constituent bytes are in big endian order. |
| */ |
| public final class ParsableByteArray { |
| |
| private byte[] data; |
| private int position; |
| // TODO(internal b/147657250): Enforce this limit on all read methods. |
| private int limit; |
| |
| /** Creates a new instance that initially has no backing data. */ |
| public ParsableByteArray() { |
| data = Util.EMPTY_BYTE_ARRAY; |
| } |
| |
| /** |
| * Creates a new instance with {@code limit} bytes and sets the limit. |
| * |
| * @param limit The limit to set. |
| */ |
| public ParsableByteArray(int limit) { |
| this.data = new byte[limit]; |
| this.limit = limit; |
| } |
| |
| /** |
| * Creates a new instance wrapping {@code data}, and sets the limit to {@code data.length}. |
| * |
| * @param data The array to wrap. |
| */ |
| public ParsableByteArray(byte[] data) { |
| this.data = data; |
| limit = data.length; |
| } |
| |
| /** |
| * Creates a new instance that wraps an existing array. |
| * |
| * @param data The data to wrap. |
| * @param limit The limit to set. |
| */ |
| public ParsableByteArray(byte[] data, int limit) { |
| this.data = data; |
| this.limit = limit; |
| } |
| |
| /** |
| * Resets the position to zero and the limit to the specified value. This might replace or wipe |
| * the {@link #getData() underlying array}, potentially invalidating any local references. |
| * |
| * @param limit The limit to set. |
| */ |
| public void reset(int limit) { |
| reset(capacity() < limit ? new byte[limit] : data, limit); |
| } |
| |
| /** |
| * Updates the instance to wrap {@code data}, and resets the position to zero and the limit to |
| * {@code data.length}. |
| * |
| * @param data The array to wrap. |
| */ |
| public void reset(byte[] data) { |
| reset(data, data.length); |
| } |
| |
| /** |
| * Updates the instance to wrap {@code data}, and resets the position to zero. |
| * |
| * @param data The array to wrap. |
| * @param limit The limit to set. |
| */ |
| public void reset(byte[] data, int limit) { |
| this.data = data; |
| this.limit = limit; |
| position = 0; |
| } |
| |
| /** |
| * Ensures the backing array is at least {@code requiredCapacity} long. |
| * |
| * <p>{@link #getPosition() position}, {@link #limit() limit}, and all data in the underlying |
| * array (including that beyond {@link #limit()}) are preserved. |
| * |
| * <p>This might replace or wipe the {@link #getData() underlying array}, potentially invalidating |
| * any local references. |
| */ |
| public void ensureCapacity(int requiredCapacity) { |
| if (requiredCapacity > capacity()) { |
| data = Arrays.copyOf(data, requiredCapacity); |
| } |
| } |
| |
| /** Returns the number of bytes yet to be read. */ |
| public int bytesLeft() { |
| return limit - position; |
| } |
| |
| /** Returns the limit. */ |
| public int limit() { |
| return limit; |
| } |
| |
| /** |
| * Sets the limit. |
| * |
| * @param limit The limit to set. |
| */ |
| public void setLimit(int limit) { |
| Assertions.checkArgument(limit >= 0 && limit <= data.length); |
| this.limit = limit; |
| } |
| |
| /** Returns the current offset in the array, in bytes. */ |
| public int getPosition() { |
| return position; |
| } |
| |
| /** |
| * Sets the reading offset in the array. |
| * |
| * @param position Byte offset in the array from which to read. |
| * @throws IllegalArgumentException Thrown if the new position is neither in nor at the end of the |
| * array. |
| */ |
| public void setPosition(int position) { |
| // It is fine for position to be at the end of the array. |
| Assertions.checkArgument(position >= 0 && position <= limit); |
| this.position = position; |
| } |
| |
| /** |
| * Returns the underlying array. |
| * |
| * <p>Changes to this array are reflected in the results of the {@code read...()} methods. |
| * |
| * <p>This reference must be assumed to become invalid when {@link #reset} or {@link |
| * #ensureCapacity} are called (because the array might get reallocated). |
| */ |
| public byte[] getData() { |
| return data; |
| } |
| |
| /** Returns the capacity of the array, which may be larger than the limit. */ |
| public int capacity() { |
| return data.length; |
| } |
| |
| /** |
| * Moves the reading offset by {@code bytes}. |
| * |
| * @param bytes The number of bytes to skip. |
| * @throws IllegalArgumentException Thrown if the new position is neither in nor at the end of the |
| * array. |
| */ |
| public void skipBytes(int bytes) { |
| setPosition(position + bytes); |
| } |
| |
| /** |
| * Reads the next {@code length} bytes into {@code bitArray}, and resets the position of {@code |
| * bitArray} to zero. |
| * |
| * @param bitArray The {@link ParsableBitArray} into which the bytes should be read. |
| * @param length The number of bytes to write. |
| */ |
| public void readBytes(ParsableBitArray bitArray, int length) { |
| readBytes(bitArray.data, 0, length); |
| bitArray.setPosition(0); |
| } |
| |
| /** |
| * Reads the next {@code length} bytes into {@code buffer} at {@code offset}. |
| * |
| * @see System#arraycopy(Object, int, Object, int, int) |
| * @param buffer The array into which the read data should be written. |
| * @param offset The offset in {@code buffer} at which the read data should be written. |
| * @param length The number of bytes to read. |
| */ |
| public void readBytes(byte[] buffer, int offset, int length) { |
| System.arraycopy(data, position, buffer, offset, length); |
| position += length; |
| } |
| |
| /** |
| * Reads the next {@code length} bytes into {@code buffer}. |
| * |
| * @see ByteBuffer#put(byte[], int, int) |
| * @param buffer The {@link ByteBuffer} into which the read data should be written. |
| * @param length The number of bytes to read. |
| */ |
| public void readBytes(ByteBuffer buffer, int length) { |
| buffer.put(data, position, length); |
| position += length; |
| } |
| |
| /** Peeks at the next byte as an unsigned value. */ |
| public int peekUnsignedByte() { |
| return (data[position] & 0xFF); |
| } |
| |
| /** Peeks at the next char. */ |
| public char peekChar() { |
| return (char) ((data[position] & 0xFF) << 8 | (data[position + 1] & 0xFF)); |
| } |
| |
| /** Reads the next byte as an unsigned value. */ |
| public int readUnsignedByte() { |
| return (data[position++] & 0xFF); |
| } |
| |
| /** Reads the next two bytes as an unsigned value. */ |
| public int readUnsignedShort() { |
| return (data[position++] & 0xFF) << 8 | (data[position++] & 0xFF); |
| } |
| |
| /** Reads the next two bytes as an unsigned value. */ |
| public int readLittleEndianUnsignedShort() { |
| return (data[position++] & 0xFF) | (data[position++] & 0xFF) << 8; |
| } |
| |
| /** Reads the next two bytes as a signed value. */ |
| public short readShort() { |
| return (short) ((data[position++] & 0xFF) << 8 | (data[position++] & 0xFF)); |
| } |
| |
| /** Reads the next two bytes as a signed value. */ |
| public short readLittleEndianShort() { |
| return (short) ((data[position++] & 0xFF) | (data[position++] & 0xFF) << 8); |
| } |
| |
| /** Reads the next three bytes as an unsigned value. */ |
| public int readUnsignedInt24() { |
| return (data[position++] & 0xFF) << 16 |
| | (data[position++] & 0xFF) << 8 |
| | (data[position++] & 0xFF); |
| } |
| |
| /** Reads the next three bytes as a signed value. */ |
| public int readInt24() { |
| return ((data[position++] & 0xFF) << 24) >> 8 |
| | (data[position++] & 0xFF) << 8 |
| | (data[position++] & 0xFF); |
| } |
| |
| /** Reads the next three bytes as a signed value in little endian order. */ |
| public int readLittleEndianInt24() { |
| return (data[position++] & 0xFF) |
| | (data[position++] & 0xFF) << 8 |
| | (data[position++] & 0xFF) << 16; |
| } |
| |
| /** Reads the next three bytes as an unsigned value in little endian order. */ |
| public int readLittleEndianUnsignedInt24() { |
| return (data[position++] & 0xFF) |
| | (data[position++] & 0xFF) << 8 |
| | (data[position++] & 0xFF) << 16; |
| } |
| |
| /** Reads the next four bytes as an unsigned value. */ |
| public long readUnsignedInt() { |
| return (data[position++] & 0xFFL) << 24 |
| | (data[position++] & 0xFFL) << 16 |
| | (data[position++] & 0xFFL) << 8 |
| | (data[position++] & 0xFFL); |
| } |
| |
| /** Reads the next four bytes as an unsigned value in little endian order. */ |
| public long readLittleEndianUnsignedInt() { |
| return (data[position++] & 0xFFL) |
| | (data[position++] & 0xFFL) << 8 |
| | (data[position++] & 0xFFL) << 16 |
| | (data[position++] & 0xFFL) << 24; |
| } |
| |
| /** Reads the next four bytes as a signed value */ |
| public int readInt() { |
| return (data[position++] & 0xFF) << 24 |
| | (data[position++] & 0xFF) << 16 |
| | (data[position++] & 0xFF) << 8 |
| | (data[position++] & 0xFF); |
| } |
| |
| /** Reads the next four bytes as a signed value in little endian order. */ |
| public int readLittleEndianInt() { |
| return (data[position++] & 0xFF) |
| | (data[position++] & 0xFF) << 8 |
| | (data[position++] & 0xFF) << 16 |
| | (data[position++] & 0xFF) << 24; |
| } |
| |
| /** Reads the next eight bytes as a signed value. */ |
| public long readLong() { |
| return (data[position++] & 0xFFL) << 56 |
| | (data[position++] & 0xFFL) << 48 |
| | (data[position++] & 0xFFL) << 40 |
| | (data[position++] & 0xFFL) << 32 |
| | (data[position++] & 0xFFL) << 24 |
| | (data[position++] & 0xFFL) << 16 |
| | (data[position++] & 0xFFL) << 8 |
| | (data[position++] & 0xFFL); |
| } |
| |
| /** Reads the next eight bytes as a signed value in little endian order. */ |
| public long readLittleEndianLong() { |
| return (data[position++] & 0xFFL) |
| | (data[position++] & 0xFFL) << 8 |
| | (data[position++] & 0xFFL) << 16 |
| | (data[position++] & 0xFFL) << 24 |
| | (data[position++] & 0xFFL) << 32 |
| | (data[position++] & 0xFFL) << 40 |
| | (data[position++] & 0xFFL) << 48 |
| | (data[position++] & 0xFFL) << 56; |
| } |
| |
| /** Reads the next four bytes, returning the integer portion of the fixed point 16.16 integer. */ |
| public int readUnsignedFixedPoint1616() { |
| int result = (data[position++] & 0xFF) << 8 | (data[position++] & 0xFF); |
| position += 2; // Skip the non-integer portion. |
| return result; |
| } |
| |
| /** |
| * Reads a Synchsafe integer. |
| * |
| * <p>Synchsafe integers keep the highest bit of every byte zeroed. A 32 bit synchsafe integer can |
| * store 28 bits of information. |
| * |
| * @return The parsed value. |
| */ |
| public int readSynchSafeInt() { |
| int b1 = readUnsignedByte(); |
| int b2 = readUnsignedByte(); |
| int b3 = readUnsignedByte(); |
| int b4 = readUnsignedByte(); |
| return (b1 << 21) | (b2 << 14) | (b3 << 7) | b4; |
| } |
| |
| /** |
| * Reads the next four bytes as an unsigned integer into an integer, if the top bit is a zero. |
| * |
| * @throws IllegalStateException Thrown if the top bit of the input data is set. |
| */ |
| public int readUnsignedIntToInt() { |
| int result = readInt(); |
| if (result < 0) { |
| throw new IllegalStateException("Top bit not zero: " + result); |
| } |
| return result; |
| } |
| |
| /** |
| * Reads the next four bytes as a little endian unsigned integer into an integer, if the top bit |
| * is a zero. |
| * |
| * @throws IllegalStateException Thrown if the top bit of the input data is set. |
| */ |
| public int readLittleEndianUnsignedIntToInt() { |
| int result = readLittleEndianInt(); |
| if (result < 0) { |
| throw new IllegalStateException("Top bit not zero: " + result); |
| } |
| return result; |
| } |
| |
| /** |
| * Reads the next eight bytes as an unsigned long into a long, if the top bit is a zero. |
| * |
| * @throws IllegalStateException Thrown if the top bit of the input data is set. |
| */ |
| public long readUnsignedLongToLong() { |
| long result = readLong(); |
| if (result < 0) { |
| throw new IllegalStateException("Top bit not zero: " + result); |
| } |
| return result; |
| } |
| |
| /** Reads the next four bytes as a 32-bit floating point value. */ |
| public float readFloat() { |
| return Float.intBitsToFloat(readInt()); |
| } |
| |
| /** Reads the next eight bytes as a 64-bit floating point value. */ |
| public double readDouble() { |
| return Double.longBitsToDouble(readLong()); |
| } |
| |
| /** |
| * Reads the next {@code length} bytes as UTF-8 characters. |
| * |
| * @param length The number of bytes to read. |
| * @return The string encoded by the bytes. |
| */ |
| public String readString(int length) { |
| return readString(length, Charsets.UTF_8); |
| } |
| |
| /** |
| * Reads the next {@code length} bytes as characters in the specified {@link Charset}. |
| * |
| * @param length The number of bytes to read. |
| * @param charset The character set of the encoded characters. |
| * @return The string encoded by the bytes in the specified character set. |
| */ |
| public String readString(int length, Charset charset) { |
| String result = new String(data, position, length, charset); |
| position += length; |
| return result; |
| } |
| |
| /** |
| * Reads the next {@code length} bytes as UTF-8 characters. A terminating NUL byte is discarded, |
| * if present. |
| * |
| * @param length The number of bytes to read. |
| * @return The string, not including any terminating NUL byte. |
| */ |
| public String readNullTerminatedString(int length) { |
| if (length == 0) { |
| return ""; |
| } |
| int stringLength = length; |
| int lastIndex = position + length - 1; |
| if (lastIndex < limit && data[lastIndex] == 0) { |
| stringLength--; |
| } |
| String result = Util.fromUtf8Bytes(data, position, stringLength); |
| position += length; |
| return result; |
| } |
| |
| /** |
| * Reads up to the next NUL byte (or the limit) as UTF-8 characters. |
| * |
| * @return The string not including any terminating NUL byte, or null if the end of the data has |
| * already been reached. |
| */ |
| @Nullable |
| public String readNullTerminatedString() { |
| return readDelimiterTerminatedString('\0'); |
| } |
| |
| /** |
| * Reads up to the next delimiter byte (or the limit) as UTF-8 characters. |
| * |
| * @return The string not including any terminating delimiter byte, or null if the end of the data |
| * has already been reached. |
| */ |
| @Nullable |
| public String readDelimiterTerminatedString(char delimiter) { |
| if (bytesLeft() == 0) { |
| return null; |
| } |
| int stringLimit = position; |
| while (stringLimit < limit && data[stringLimit] != delimiter) { |
| stringLimit++; |
| } |
| String string = Util.fromUtf8Bytes(data, position, stringLimit - position); |
| position = stringLimit; |
| if (position < limit) { |
| position++; |
| } |
| return string; |
| } |
| |
| /** |
| * Reads a line of text. |
| * |
| * <p>A line is considered to be terminated by any one of a carriage return ('\r'), a line feed |
| * ('\n'), or a carriage return followed immediately by a line feed ('\r\n'). The UTF-8 charset is |
| * used. This method discards leading UTF-8 byte order marks, if present. |
| * |
| * @return The line not including any line-termination characters, or null if the end of the data |
| * has already been reached. |
| */ |
| @Nullable |
| public String readLine() { |
| if (bytesLeft() == 0) { |
| return null; |
| } |
| int lineLimit = position; |
| while (lineLimit < limit && !Util.isLinebreak(data[lineLimit])) { |
| lineLimit++; |
| } |
| if (lineLimit - position >= 3 |
| && data[position] == (byte) 0xEF |
| && data[position + 1] == (byte) 0xBB |
| && data[position + 2] == (byte) 0xBF) { |
| // There's a UTF-8 byte order mark at the start of the line. Discard it. |
| position += 3; |
| } |
| String line = Util.fromUtf8Bytes(data, position, lineLimit - position); |
| position = lineLimit; |
| if (position == limit) { |
| return line; |
| } |
| if (data[position] == '\r') { |
| position++; |
| if (position == limit) { |
| return line; |
| } |
| } |
| if (data[position] == '\n') { |
| position++; |
| } |
| return line; |
| } |
| |
| /** |
| * Reads a long value encoded by UTF-8 encoding |
| * |
| * @throws NumberFormatException if there is a problem with decoding |
| * @return Decoded long value |
| */ |
| public long readUtf8EncodedLong() { |
| int length = 0; |
| long value = data[position]; |
| // find the high most 0 bit |
| for (int j = 7; j >= 0; j--) { |
| if ((value & (1 << j)) == 0) { |
| if (j < 6) { |
| value &= (1 << j) - 1; |
| length = 7 - j; |
| } else if (j == 7) { |
| length = 1; |
| } |
| break; |
| } |
| } |
| if (length == 0) { |
| throw new NumberFormatException("Invalid UTF-8 sequence first byte: " + value); |
| } |
| for (int i = 1; i < length; i++) { |
| int x = data[position + i]; |
| if ((x & 0xC0) != 0x80) { // if the high most 0 bit not 7th |
| throw new NumberFormatException("Invalid UTF-8 sequence continuation byte: " + value); |
| } |
| value = (value << 6) | (x & 0x3F); |
| } |
| position += length; |
| return value; |
| } |
| } |