mirror of
https://github.com/androidx/media.git
synced 2025-05-11 09:39:52 +08:00
Optimize ParsableBitArray
ParsableBitArray.readBit in particular was doing an excessive amount of work. The new implementation is ~20% faster on desktop. Issue: #3040 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=161666420
This commit is contained in:
parent
2b1614cc7b
commit
01c0ccbdbd
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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 android.test.MoreAsserts;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ParsableBitArray}.
|
||||||
|
*/
|
||||||
|
public final class ParsableBitArrayTest extends TestCase {
|
||||||
|
|
||||||
|
private static final byte[] TEST_DATA = new byte[] {0x3C, (byte) 0xD2, (byte) 0x5F, (byte) 0x01,
|
||||||
|
(byte) 0xFF, (byte) 0x14, (byte) 0x60, (byte) 0x99};
|
||||||
|
|
||||||
|
public void testReadAllBytes() {
|
||||||
|
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
|
||||||
|
byte[] bytesRead = new byte[TEST_DATA.length];
|
||||||
|
testArray.readBytes(bytesRead, 0, TEST_DATA.length);
|
||||||
|
MoreAsserts.assertEquals(TEST_DATA, bytesRead);
|
||||||
|
assertEquals(TEST_DATA.length * 8, testArray.getPosition());
|
||||||
|
assertEquals(TEST_DATA.length, testArray.getBytePosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testReadBit() {
|
||||||
|
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
|
||||||
|
assertReadBitsToEnd(0, testArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testReadBits() {
|
||||||
|
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
|
||||||
|
assertEquals(getTestDataBits(0, 5), testArray.readBits(5));
|
||||||
|
assertEquals(getTestDataBits(5, 3), testArray.readBits(3));
|
||||||
|
assertEquals(getTestDataBits(8, 16), testArray.readBits(16));
|
||||||
|
assertEquals(getTestDataBits(24, 3), testArray.readBits(3));
|
||||||
|
assertEquals(getTestDataBits(27, 18), testArray.readBits(18));
|
||||||
|
assertEquals(getTestDataBits(45, 5), testArray.readBits(5));
|
||||||
|
assertEquals(getTestDataBits(50, 14), testArray.readBits(14));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRead32BitsByteAligned() {
|
||||||
|
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
|
||||||
|
assertEquals(getTestDataBits(0, 32), testArray.readBits(32));
|
||||||
|
assertEquals(getTestDataBits(32, 32), testArray.readBits(32));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRead32BitsNonByteAligned() {
|
||||||
|
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
|
||||||
|
assertEquals(getTestDataBits(0, 5), testArray.readBits(5));
|
||||||
|
assertEquals(getTestDataBits(5, 32), testArray.readBits(32));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSkipBytes() {
|
||||||
|
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
|
||||||
|
testArray.skipBytes(2);
|
||||||
|
assertReadBitsToEnd(16, testArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSkipBitsByteAligned() {
|
||||||
|
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
|
||||||
|
testArray.skipBits(16);
|
||||||
|
assertReadBitsToEnd(16, testArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSkipBitsNonByteAligned() {
|
||||||
|
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
|
||||||
|
testArray.skipBits(5);
|
||||||
|
assertReadBitsToEnd(5, testArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSetPositionByteAligned() {
|
||||||
|
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
|
||||||
|
testArray.setPosition(16);
|
||||||
|
assertReadBitsToEnd(16, testArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSetPositionNonByteAligned() {
|
||||||
|
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
|
||||||
|
testArray.setPosition(5);
|
||||||
|
assertReadBitsToEnd(5, testArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testByteAlignFromNonByteAligned() {
|
||||||
|
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
|
||||||
|
testArray.setPosition(11);
|
||||||
|
testArray.byteAlign();
|
||||||
|
assertEquals(2, testArray.getBytePosition());
|
||||||
|
assertEquals(16, testArray.getPosition());
|
||||||
|
assertReadBitsToEnd(16, testArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testByteAlignFromByteAligned() {
|
||||||
|
ParsableBitArray testArray = new ParsableBitArray(TEST_DATA);
|
||||||
|
testArray.setPosition(16);
|
||||||
|
testArray.byteAlign(); // Should be a no-op.
|
||||||
|
assertEquals(2, testArray.getBytePosition());
|
||||||
|
assertEquals(16, testArray.getPosition());
|
||||||
|
assertReadBitsToEnd(16, testArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertReadBitsToEnd(int expectedStartPosition, ParsableBitArray testArray) {
|
||||||
|
int position = testArray.getPosition();
|
||||||
|
assertEquals(expectedStartPosition, position);
|
||||||
|
for (int i = position; i < TEST_DATA.length * 8; i++) {
|
||||||
|
assertEquals(getTestDataBit(i), testArray.readBit());
|
||||||
|
assertEquals(i + 1, testArray.getPosition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getTestDataBits(int bitPosition, int length) {
|
||||||
|
int result = 0;
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
result = result << 1;
|
||||||
|
if (getTestDataBit(bitPosition++)) {
|
||||||
|
result |= 0x1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean getTestDataBit(int bitPosition) {
|
||||||
|
return (TEST_DATA[bitPosition / 8] & (0x80 >>> (bitPosition % 8))) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -110,14 +110,26 @@ public final class ParsableBitArray {
|
|||||||
assertValidOffset();
|
assertValidOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skips a single bit.
|
||||||
|
*/
|
||||||
|
public void skipBit() {
|
||||||
|
if (++bitOffset == 8) {
|
||||||
|
bitOffset = 0;
|
||||||
|
byteOffset++;
|
||||||
|
}
|
||||||
|
assertValidOffset();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Skips bits and moves current reading position forward.
|
* Skips bits and moves current reading position forward.
|
||||||
*
|
*
|
||||||
* @param n The number of bits to skip.
|
* @param numBits The number of bits to skip.
|
||||||
*/
|
*/
|
||||||
public void skipBits(int n) {
|
public void skipBits(int numBits) {
|
||||||
byteOffset += (n / 8);
|
int numBytes = numBits / 8;
|
||||||
bitOffset += (n % 8);
|
byteOffset += numBytes;
|
||||||
|
bitOffset += numBits - (numBytes * 8);
|
||||||
if (bitOffset > 7) {
|
if (bitOffset > 7) {
|
||||||
byteOffset++;
|
byteOffset++;
|
||||||
bitOffset -= 8;
|
bitOffset -= 8;
|
||||||
@ -131,7 +143,9 @@ public final class ParsableBitArray {
|
|||||||
* @return Whether the bit is set.
|
* @return Whether the bit is set.
|
||||||
*/
|
*/
|
||||||
public boolean readBit() {
|
public boolean readBit() {
|
||||||
return readBits(1) == 1;
|
boolean returnValue = (data[byteOffset] & (0x80 >> bitOffset)) != 0;
|
||||||
|
skipBit();
|
||||||
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -141,48 +155,18 @@ public final class ParsableBitArray {
|
|||||||
* @return An integer whose bottom n bits hold the read data.
|
* @return An integer whose bottom n bits hold the read data.
|
||||||
*/
|
*/
|
||||||
public int readBits(int numBits) {
|
public int readBits(int numBits) {
|
||||||
if (numBits == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int returnValue = 0;
|
int returnValue = 0;
|
||||||
|
bitOffset += numBits;
|
||||||
// Read as many whole bytes as we can.
|
while (bitOffset > 8) {
|
||||||
int wholeBytes = (numBits / 8);
|
bitOffset -= 8;
|
||||||
for (int i = 0; i < wholeBytes; i++) {
|
returnValue |= (data[byteOffset++] & 0xFF) << bitOffset;
|
||||||
int byteValue;
|
}
|
||||||
if (bitOffset != 0) {
|
returnValue |= (data[byteOffset] & 0xFF) >> 8 - bitOffset;
|
||||||
byteValue = ((data[byteOffset] & 0xFF) << bitOffset)
|
returnValue &= 0xFFFFFFFF >>> (32 - numBits);
|
||||||
| ((data[byteOffset + 1] & 0xFF) >>> (8 - bitOffset));
|
if (bitOffset == 8) {
|
||||||
} else {
|
bitOffset = 0;
|
||||||
byteValue = data[byteOffset];
|
|
||||||
}
|
|
||||||
numBits -= 8;
|
|
||||||
returnValue |= (byteValue & 0xFF) << numBits;
|
|
||||||
byteOffset++;
|
byteOffset++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read any remaining bits.
|
|
||||||
if (numBits > 0) {
|
|
||||||
int nextBit = bitOffset + numBits;
|
|
||||||
byte writeMask = (byte) (0xFF >> (8 - numBits));
|
|
||||||
|
|
||||||
if (nextBit > 8) {
|
|
||||||
// Combine bits from current byte and next byte.
|
|
||||||
returnValue |= ((((data[byteOffset] & 0xFF) << (nextBit - 8)
|
|
||||||
| ((data[byteOffset + 1] & 0xFF) >> (16 - nextBit))) & writeMask));
|
|
||||||
byteOffset++;
|
|
||||||
} else {
|
|
||||||
// Bits to be read only within current byte.
|
|
||||||
returnValue |= (((data[byteOffset] & 0xFF) >> (8 - nextBit)) & writeMask);
|
|
||||||
if (nextBit == 8) {
|
|
||||||
byteOffset++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bitOffset = nextBit % 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
assertValidOffset();
|
assertValidOffset();
|
||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
@ -231,7 +215,6 @@ public final class ParsableBitArray {
|
|||||||
private void assertValidOffset() {
|
private void assertValidOffset() {
|
||||||
// It is fine for position to be at the end of the array, but no further.
|
// It is fine for position to be at the end of the array, but no further.
|
||||||
Assertions.checkState(byteOffset >= 0
|
Assertions.checkState(byteOffset >= 0
|
||||||
&& (bitOffset >= 0 && bitOffset < 8)
|
|
||||||
&& (byteOffset < byteLimit || (byteOffset == byteLimit && bitOffset == 0)));
|
&& (byteOffset < byteLimit || (byteOffset == byteLimit && bitOffset == 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user