Add ParsableByteArray#ensureCapacity() method that keeps data

#exofixit

PiperOrigin-RevId: 344845328
This commit is contained in:
ibaker 2020-11-30 18:52:32 +00:00 committed by Oliver Woodman
parent 7e635d9560
commit 84a7ffc12a
6 changed files with 60 additions and 25 deletions

View File

@ -19,6 +19,7 @@ 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
@ -100,8 +101,21 @@ public final class ParsableByteArray {
}
/**
* Returns the number of bytes yet to be read.
* 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;
}
@ -148,8 +162,8 @@ public final class ParsableByteArray {
*
* <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} is called (because the
* array might get reallocated).
* <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;

View File

@ -2214,9 +2214,8 @@ public final class Util {
if (input.bytesLeft() <= 0) {
return false;
}
byte[] outputData = output.getData();
if (outputData.length < input.bytesLeft()) {
outputData = new byte[2 * input.bytesLeft()];
if (output.capacity() < input.bytesLeft()) {
output.ensureCapacity(2 * input.bytesLeft());
}
if (inflater == null) {
inflater = new Inflater();
@ -2225,16 +2224,17 @@ public final class Util {
try {
int outputSize = 0;
while (true) {
outputSize += inflater.inflate(outputData, outputSize, outputData.length - outputSize);
outputSize +=
inflater.inflate(output.getData(), outputSize, output.capacity() - outputSize);
if (inflater.finished()) {
output.reset(outputData, outputSize);
output.setLimit(outputSize);
return true;
}
if (inflater.needsDictionary() || inflater.needsInput()) {
return false;
}
if (outputSize == outputData.length) {
outputData = Arrays.copyOf(outputData, outputData.length * 2);
if (outputSize == output.capacity()) {
output.ensureCapacity(output.capacity() * 2);
}
}
} catch (DataFormatException e) {

View File

@ -20,6 +20,7 @@ import static java.nio.charset.Charset.forName;
import static org.junit.Assert.fail;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.primitives.Bytes;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.junit.Test;
@ -38,6 +39,36 @@ public final class ParsableByteArrayTest {
return testArray;
}
@Test
public void ensureCapacity_doesntReallocateNeedlesslyAndPreservesPositionAndLimit() {
ParsableByteArray array = getTestDataArray();
byte[] dataBefore = array.getData();
byte[] copyOfDataBefore = dataBefore.clone();
array.setPosition(3);
array.setLimit(4);
array.ensureCapacity(array.capacity() - 1);
assertThat(array.getData()).isSameInstanceAs(dataBefore);
assertThat(array.getData()).isEqualTo(copyOfDataBefore);
assertThat(array.getPosition()).isEqualTo(3);
assertThat(array.limit()).isEqualTo(4);
}
@Test
public void ensureCapacity_preservesDataPositionAndLimitWhenReallocating() {
ParsableByteArray array = getTestDataArray();
byte[] copyOfDataBefore = array.getData().clone();
array.setPosition(3);
array.setLimit(4);
array.ensureCapacity(array.capacity() + 1);
assertThat(array.getData()).isEqualTo(Bytes.concat(copyOfDataBefore, new byte[] {0}));
assertThat(array.getPosition()).isEqualTo(3);
assertThat(array.limit()).isEqualTo(4);
}
@Test
public void readShort() {
testReadShort((short) -1);

View File

@ -1341,9 +1341,7 @@ public class MatroskaExtractor implements Extractor {
return;
}
if (scratch.capacity() < requiredLength) {
scratch.reset(
Arrays.copyOf(scratch.getData(), max(scratch.getData().length * 2, requiredLength)),
scratch.limit());
scratch.ensureCapacity(max(scratch.capacity() * 2, requiredLength));
}
input.readFully(scratch.getData(), scratch.limit(), requiredLength - scratch.limit());
scratch.setLimit(requiredLength);

View File

@ -87,11 +87,7 @@ import java.util.Arrays;
int size = calculatePacketSize(currentSegmentIndex);
int segmentIndex = currentSegmentIndex + segmentCount;
if (size > 0) {
if (packetArray.capacity() < packetArray.limit() + size) {
packetArray.reset(
Arrays.copyOf(packetArray.getData(), packetArray.limit() + size),
/* limit= */ packetArray.limit());
}
packetArray.ensureCapacity(packetArray.limit() + size);
input.readFully(packetArray.getData(), packetArray.limit(), size);
packetArray.setLimit(packetArray.limit() + size);
populated = pageHeader.laces[segmentIndex - 1] != 255;

View File

@ -23,7 +23,6 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.TimestampAdjuster;
import com.google.android.exoplayer2.util.Util;
import java.util.Arrays;
/**
* Reads section data packets and feeds the whole sections to a given {@link SectionPayloadReader}.
@ -107,12 +106,9 @@ public final class SectionReader implements TsPayloadReader {
(((secondHeaderByte & 0x0F) << 8) | thirdHeaderByte) + SECTION_HEADER_LENGTH;
if (sectionData.capacity() < totalSectionLength) {
// Ensure there is enough space to keep the whole section.
byte[] bytes = sectionData.getData();
int limit = min(MAX_SECTION_LENGTH, max(totalSectionLength, bytes.length * 2));
if (limit > bytes.length) {
bytes = Arrays.copyOf(sectionData.getData(), limit);
}
sectionData.reset(bytes, limit);
int limit =
min(MAX_SECTION_LENGTH, max(totalSectionLength, sectionData.capacity() * 2));
sectionData.ensureCapacity(limit);
}
}
} else {