Add peek() method to ExtractorInput
PiperOrigin-RevId: 284586799
This commit is contained in:
parent
bcb51ec155
commit
70ba4b197c
@ -58,7 +58,9 @@ public final class DefaultExtractorInput implements ExtractorInput {
|
|||||||
public int read(byte[] target, int offset, int length) throws IOException, InterruptedException {
|
public int read(byte[] target, int offset, int length) throws IOException, InterruptedException {
|
||||||
int bytesRead = readFromPeekBuffer(target, offset, length);
|
int bytesRead = readFromPeekBuffer(target, offset, length);
|
||||||
if (bytesRead == 0) {
|
if (bytesRead == 0) {
|
||||||
bytesRead = readFromDataSource(target, offset, length, 0, true);
|
bytesRead =
|
||||||
|
readFromDataSource(
|
||||||
|
target, offset, length, /* bytesAlreadyRead= */ 0, /* allowEndOfInput= */ true);
|
||||||
}
|
}
|
||||||
commitBytesRead(bytesRead);
|
commitBytesRead(bytesRead);
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
@ -110,6 +112,31 @@ public final class DefaultExtractorInput implements ExtractorInput {
|
|||||||
skipFully(length, false);
|
skipFully(length, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int peek(byte[] target, int offset, int length) throws IOException, InterruptedException {
|
||||||
|
ensureSpaceForPeek(length);
|
||||||
|
int peekBufferRemainingBytes = peekBufferLength - peekBufferPosition;
|
||||||
|
int bytesPeeked;
|
||||||
|
if (peekBufferRemainingBytes == 0) {
|
||||||
|
bytesPeeked =
|
||||||
|
readFromDataSource(
|
||||||
|
peekBuffer,
|
||||||
|
peekBufferPosition,
|
||||||
|
length,
|
||||||
|
/* bytesAlreadyRead= */ 0,
|
||||||
|
/* allowEndOfInput= */ true);
|
||||||
|
if (bytesPeeked == C.RESULT_END_OF_INPUT) {
|
||||||
|
return C.RESULT_END_OF_INPUT;
|
||||||
|
}
|
||||||
|
peekBufferLength += bytesPeeked;
|
||||||
|
} else {
|
||||||
|
bytesPeeked = Math.min(length, peekBufferRemainingBytes);
|
||||||
|
}
|
||||||
|
System.arraycopy(peekBuffer, peekBufferPosition, target, offset, bytesPeeked);
|
||||||
|
peekBufferPosition += bytesPeeked;
|
||||||
|
return bytesPeeked;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean peekFully(byte[] target, int offset, int length, boolean allowEndOfInput)
|
public boolean peekFully(byte[] target, int offset, int length, boolean allowEndOfInput)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
@ -201,7 +228,7 @@ public final class DefaultExtractorInput implements ExtractorInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads from the peek buffer
|
* Reads from the peek buffer.
|
||||||
*
|
*
|
||||||
* @param target A target array into which data should be written.
|
* @param target A target array into which data should be written.
|
||||||
* @param offset The offset into the target array at which to write.
|
* @param offset The offset into the target array at which to write.
|
||||||
|
@ -27,19 +27,19 @@ import java.io.InputStream;
|
|||||||
* for more info about each mode.
|
* for more info about each mode.
|
||||||
*
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>The {@code read()} and {@code skip()} methods provide {@link InputStream}-like byte-level
|
* <li>The {@code read()/peek()} and {@code skip()} methods provide {@link InputStream}-like
|
||||||
* access operations.
|
* byte-level access operations.
|
||||||
* <li>The {@code read/skip/peekFully()} and {@code advancePeekPosition()} methods assume the user
|
* <li>The {@code read/skip/peekFully()} and {@code advancePeekPosition()} methods assume the user
|
||||||
* wants to read an entire block/frame/header of known length.
|
* wants to read an entire block/frame/header of known length.
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <h3>{@link InputStream}-like methods</h3>
|
* <h3>{@link InputStream}-like methods</h3>
|
||||||
*
|
*
|
||||||
* <p>The {@code read()} and {@code skip()} methods provide {@link InputStream}-like byte-level
|
* <p>The {@code read()/peek()} and {@code skip()} methods provide {@link InputStream}-like
|
||||||
* access operations. The {@code length} parameter is a maximum, and each method returns the number
|
* byte-level access operations. The {@code length} parameter is a maximum, and each method returns
|
||||||
* of bytes actually processed. This may be less than {@code length} because the end of the input
|
* the number of bytes actually processed. This may be less than {@code length} because the end of
|
||||||
* was reached, or the method was interrupted, or the operation was aborted early for another
|
* the input was reached, or the method was interrupted, or the operation was aborted early for
|
||||||
* reason.
|
* another reason.
|
||||||
*
|
*
|
||||||
* <h3>Block-based methods</h3>
|
* <h3>Block-based methods</h3>
|
||||||
*
|
*
|
||||||
@ -102,7 +102,8 @@ public interface ExtractorInput {
|
|||||||
throws IOException, InterruptedException;
|
throws IOException, InterruptedException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Equivalent to {@code readFully(target, offset, length, false)}.
|
* Equivalent to {@link #readFully(byte[], int, int, boolean) readFully(target, offset, length,
|
||||||
|
* false)}.
|
||||||
*
|
*
|
||||||
* @param target A target array into which data should be written.
|
* @param target A target array into which data should be written.
|
||||||
* @param offset The offset into the target array at which to write.
|
* @param offset The offset into the target array at which to write.
|
||||||
@ -155,8 +156,11 @@ public interface ExtractorInput {
|
|||||||
void skipFully(int length) throws IOException, InterruptedException;
|
void skipFully(int length) throws IOException, InterruptedException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Peeks {@code length} bytes from the peek position, writing them into {@code target} at index
|
* Peeks up to {@code length} bytes from the peek position. The current read position is left
|
||||||
* {@code offset}. The current read position is left unchanged.
|
* unchanged.
|
||||||
|
*
|
||||||
|
* <p>This method blocks until at least one byte of data can be peeked, the end of the input is
|
||||||
|
* detected, or an exception is thrown.
|
||||||
*
|
*
|
||||||
* <p>Calling {@link #resetPeekPosition()} resets the peek position to equal the current read
|
* <p>Calling {@link #resetPeekPosition()} resets the peek position to equal the current read
|
||||||
* position, so the caller can peek the same data again. Reading or skipping also resets the peek
|
* position, so the caller can peek the same data again. Reading or skipping also resets the peek
|
||||||
@ -164,6 +168,18 @@ public interface ExtractorInput {
|
|||||||
*
|
*
|
||||||
* @param target A target array into which data should be written.
|
* @param target A target array into which data should be written.
|
||||||
* @param offset The offset into the target array at which to write.
|
* @param offset The offset into the target array at which to write.
|
||||||
|
* @param length The maximum number of bytes to peek from the input.
|
||||||
|
* @return The number of bytes peeked, or {@link C#RESULT_END_OF_INPUT} if the input has ended.
|
||||||
|
* @throws IOException If an error occurs peeking from the input.
|
||||||
|
* @throws InterruptedException If the thread has been interrupted.
|
||||||
|
*/
|
||||||
|
int peek(byte[] target, int offset, int length) throws IOException, InterruptedException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like {@link #peek(byte[], int, int)}, but peeks the requested {@code length} in full.
|
||||||
|
*
|
||||||
|
* @param target A target array into which data should be written.
|
||||||
|
* @param offset The offset into the target array at which to write.
|
||||||
* @param length The number of bytes to peek from the input.
|
* @param length The number of bytes to peek from the input.
|
||||||
* @param allowEndOfInput True if encountering the end of the input having peeked no data is
|
* @param allowEndOfInput True if encountering the end of the input having peeked no data is
|
||||||
* allowed, and should result in {@code false} being returned. False if it should be
|
* allowed, and should result in {@code false} being returned. False if it should be
|
||||||
@ -181,12 +197,8 @@ public interface ExtractorInput {
|
|||||||
throws IOException, InterruptedException;
|
throws IOException, InterruptedException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Peeks {@code length} bytes from the peek position, writing them into {@code target} at index
|
* Equivalent to {@link #peekFully(byte[], int, int, boolean) peekFully(target, offset, length,
|
||||||
* {@code offset}. The current read position is left unchanged.
|
* false)}.
|
||||||
* <p>
|
|
||||||
* Calling {@link #resetPeekPosition()} resets the peek position to equal the current read
|
|
||||||
* position, so the caller can peek the same data again. Reading and skipping also reset the peek
|
|
||||||
* position.
|
|
||||||
*
|
*
|
||||||
* @param target A target array into which data should be written.
|
* @param target A target array into which data should be written.
|
||||||
* @param offset The offset into the target array at which to write.
|
* @param offset The offset into the target array at which to write.
|
||||||
|
@ -49,7 +49,7 @@ public class DefaultExtractorInputTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRead() throws Exception {
|
public void testReadMultipleTimes() throws Exception {
|
||||||
DefaultExtractorInput input = createDefaultExtractorInput();
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
byte[] target = new byte[TEST_DATA.length];
|
byte[] target = new byte[TEST_DATA.length];
|
||||||
// We expect to perform three reads of three bytes, as setup in buildTestDataSource.
|
// We expect to perform three reads of three bytes, as setup in buildTestDataSource.
|
||||||
@ -60,39 +60,70 @@ public class DefaultExtractorInputTest {
|
|||||||
assertThat(bytesRead).isEqualTo(6);
|
assertThat(bytesRead).isEqualTo(6);
|
||||||
bytesRead += input.read(target, 6, TEST_DATA.length);
|
bytesRead += input.read(target, 6, TEST_DATA.length);
|
||||||
assertThat(bytesRead).isEqualTo(9);
|
assertThat(bytesRead).isEqualTo(9);
|
||||||
// Check the read data is correct.
|
assertThat(input.getPosition()).isEqualTo(9);
|
||||||
assertThat(Arrays.equals(TEST_DATA, target)).isTrue();
|
assertThat(TEST_DATA).isEqualTo(target);
|
||||||
// Check we're now indicated that the end of input is reached.
|
|
||||||
int expectedEndOfInput = input.read(target, 0, TEST_DATA.length);
|
|
||||||
assertThat(expectedEndOfInput).isEqualTo(RESULT_END_OF_INPUT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadPeeked() throws Exception {
|
public void testReadAlreadyPeeked() throws Exception {
|
||||||
DefaultExtractorInput input = createDefaultExtractorInput();
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
byte[] target = new byte[TEST_DATA.length];
|
byte[] target = new byte[TEST_DATA.length];
|
||||||
|
|
||||||
input.advancePeekPosition(TEST_DATA.length);
|
input.advancePeekPosition(TEST_DATA.length);
|
||||||
|
int bytesRead = input.read(target, 0, TEST_DATA.length - 1);
|
||||||
|
|
||||||
|
assertThat(bytesRead).isEqualTo(TEST_DATA.length - 1);
|
||||||
|
assertThat(input.getPosition()).isEqualTo(TEST_DATA.length - 1);
|
||||||
|
assertThat(Arrays.copyOf(TEST_DATA, TEST_DATA.length - 1))
|
||||||
|
.isEqualTo(Arrays.copyOf(target, TEST_DATA.length - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadPartiallyPeeked() throws Exception {
|
||||||
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
|
byte[] target = new byte[TEST_DATA.length];
|
||||||
|
|
||||||
|
input.advancePeekPosition(TEST_DATA.length - 1);
|
||||||
int bytesRead = input.read(target, 0, TEST_DATA.length);
|
int bytesRead = input.read(target, 0, TEST_DATA.length);
|
||||||
assertThat(bytesRead).isEqualTo(TEST_DATA.length);
|
|
||||||
|
|
||||||
// Check the read data is correct.
|
assertThat(bytesRead).isEqualTo(TEST_DATA.length - 1);
|
||||||
assertThat(Arrays.equals(TEST_DATA, target)).isTrue();
|
assertThat(input.getPosition()).isEqualTo(TEST_DATA.length - 1);
|
||||||
|
assertThat(Arrays.copyOf(TEST_DATA, TEST_DATA.length - 1))
|
||||||
|
.isEqualTo(Arrays.copyOf(target, TEST_DATA.length - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadMoreDataPeeked() throws Exception {
|
public void testReadEndOfInputBeforeFirstByteRead() throws Exception {
|
||||||
DefaultExtractorInput input = createDefaultExtractorInput();
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
byte[] target = new byte[TEST_DATA.length];
|
byte[] target = new byte[TEST_DATA.length];
|
||||||
|
|
||||||
input.advancePeekPosition(TEST_DATA.length);
|
input.skipFully(TEST_DATA.length);
|
||||||
|
int bytesRead = input.read(target, 0, TEST_DATA.length);
|
||||||
|
|
||||||
int bytesRead = input.read(target, 0, TEST_DATA.length + 1);
|
assertThat(bytesRead).isEqualTo(RESULT_END_OF_INPUT);
|
||||||
assertThat(bytesRead).isEqualTo(TEST_DATA.length);
|
assertThat(input.getPosition()).isEqualTo(TEST_DATA.length);
|
||||||
|
}
|
||||||
|
|
||||||
// Check the read data is correct.
|
@Test
|
||||||
assertThat(Arrays.equals(TEST_DATA, target)).isTrue();
|
public void testReadEndOfInputAfterFirstByteRead() throws Exception {
|
||||||
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
|
byte[] target = new byte[TEST_DATA.length];
|
||||||
|
|
||||||
|
input.skipFully(TEST_DATA.length - 1);
|
||||||
|
int bytesRead = input.read(target, 0, TEST_DATA.length);
|
||||||
|
|
||||||
|
assertThat(bytesRead).isEqualTo(1);
|
||||||
|
assertThat(input.getPosition()).isEqualTo(TEST_DATA.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadZeroLength() throws Exception {
|
||||||
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
|
byte[] target = new byte[TEST_DATA.length];
|
||||||
|
|
||||||
|
int bytesRead = input.read(target, /* offset= */ 0, /* length= */ 0);
|
||||||
|
|
||||||
|
assertThat(bytesRead).isEqualTo(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -101,7 +132,7 @@ public class DefaultExtractorInputTest {
|
|||||||
byte[] target = new byte[TEST_DATA.length];
|
byte[] target = new byte[TEST_DATA.length];
|
||||||
input.readFully(target, 0, TEST_DATA.length);
|
input.readFully(target, 0, TEST_DATA.length);
|
||||||
// Check that we read the whole of TEST_DATA.
|
// Check that we read the whole of TEST_DATA.
|
||||||
assertThat(Arrays.equals(TEST_DATA, target)).isTrue();
|
assertThat(TEST_DATA).isEqualTo(target);
|
||||||
assertThat(input.getPosition()).isEqualTo(TEST_DATA.length);
|
assertThat(input.getPosition()).isEqualTo(TEST_DATA.length);
|
||||||
// Check that we see end of input if we read again with allowEndOfInput set.
|
// Check that we see end of input if we read again with allowEndOfInput set.
|
||||||
boolean result = input.readFully(target, 0, 1, true);
|
boolean result = input.readFully(target, 0, 1, true);
|
||||||
@ -121,11 +152,11 @@ public class DefaultExtractorInputTest {
|
|||||||
DefaultExtractorInput input = createDefaultExtractorInput();
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
byte[] target = new byte[5];
|
byte[] target = new byte[5];
|
||||||
input.readFully(target, 0, 5);
|
input.readFully(target, 0, 5);
|
||||||
assertThat(Arrays.equals(copyOf(TEST_DATA, 5), target)).isTrue();
|
assertThat(copyOf(TEST_DATA, 5)).isEqualTo(target);
|
||||||
assertThat(input.getPosition()).isEqualTo(5);
|
assertThat(input.getPosition()).isEqualTo(5);
|
||||||
target = new byte[4];
|
target = new byte[4];
|
||||||
input.readFully(target, 0, 4);
|
input.readFully(target, 0, 4);
|
||||||
assertThat(Arrays.equals(copyOfRange(TEST_DATA, 5, 9), target)).isTrue();
|
assertThat(copyOfRange(TEST_DATA, 5, 9)).isEqualTo(target);
|
||||||
assertThat(input.getPosition()).isEqualTo(5 + 4);
|
assertThat(input.getPosition()).isEqualTo(5 + 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,27 +211,23 @@ public class DefaultExtractorInputTest {
|
|||||||
input.readFully(target, 0, TEST_DATA.length);
|
input.readFully(target, 0, TEST_DATA.length);
|
||||||
|
|
||||||
// Check the read data is correct.
|
// Check the read data is correct.
|
||||||
assertThat(Arrays.equals(TEST_DATA, target)).isTrue();
|
assertThat(TEST_DATA).isEqualTo(target);
|
||||||
assertThat(input.getPosition()).isEqualTo(TEST_DATA.length);
|
assertThat(input.getPosition()).isEqualTo(TEST_DATA.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSkip() throws Exception {
|
public void testSkipMultipleTimes() throws Exception {
|
||||||
FakeDataSource testDataSource = buildDataSource();
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
DefaultExtractorInput input = new DefaultExtractorInput(testDataSource, 0, C.LENGTH_UNSET);
|
|
||||||
// We expect to perform three skips of three bytes, as setup in buildTestDataSource.
|
// We expect to perform three skips of three bytes, as setup in buildTestDataSource.
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
assertThat(input.skip(TEST_DATA.length)).isEqualTo(3);
|
assertThat(input.skip(TEST_DATA.length)).isEqualTo(3);
|
||||||
}
|
}
|
||||||
// Check we're now indicated that the end of input is reached.
|
assertThat(input.getPosition()).isEqualTo(TEST_DATA.length);
|
||||||
int expectedEndOfInput = input.skip(TEST_DATA.length);
|
|
||||||
assertThat(expectedEndOfInput).isEqualTo(RESULT_END_OF_INPUT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLargeSkip() throws Exception {
|
public void testLargeSkip() throws Exception {
|
||||||
FakeDataSource testDataSource = buildLargeDataSource();
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
DefaultExtractorInput input = new DefaultExtractorInput(testDataSource, 0, C.LENGTH_UNSET);
|
|
||||||
// Check that skipping the entire data source succeeds.
|
// Check that skipping the entire data source succeeds.
|
||||||
int bytesToSkip = LARGE_TEST_DATA_LENGTH;
|
int bytesToSkip = LARGE_TEST_DATA_LENGTH;
|
||||||
while (bytesToSkip > 0) {
|
while (bytesToSkip > 0) {
|
||||||
@ -208,6 +235,59 @@ public class DefaultExtractorInputTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSkipAlreadyPeeked() throws Exception {
|
||||||
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
|
|
||||||
|
input.advancePeekPosition(TEST_DATA.length);
|
||||||
|
int bytesSkipped = input.skip(TEST_DATA.length - 1);
|
||||||
|
|
||||||
|
assertThat(bytesSkipped).isEqualTo(TEST_DATA.length - 1);
|
||||||
|
assertThat(input.getPosition()).isEqualTo(TEST_DATA.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSkipPartiallyPeeked() throws Exception {
|
||||||
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
|
|
||||||
|
input.advancePeekPosition(TEST_DATA.length - 1);
|
||||||
|
int bytesSkipped = input.skip(TEST_DATA.length);
|
||||||
|
|
||||||
|
assertThat(bytesSkipped).isEqualTo(TEST_DATA.length - 1);
|
||||||
|
assertThat(input.getPosition()).isEqualTo(TEST_DATA.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSkipEndOfInputBeforeFirstByteSkipped() throws Exception {
|
||||||
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
|
|
||||||
|
input.skipFully(TEST_DATA.length);
|
||||||
|
int bytesSkipped = input.skip(TEST_DATA.length);
|
||||||
|
|
||||||
|
assertThat(bytesSkipped).isEqualTo(RESULT_END_OF_INPUT);
|
||||||
|
assertThat(input.getPosition()).isEqualTo(TEST_DATA.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSkipEndOfInputAfterFirstByteSkipped() throws Exception {
|
||||||
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
|
|
||||||
|
input.skipFully(TEST_DATA.length - 1);
|
||||||
|
int bytesSkipped = input.skip(TEST_DATA.length);
|
||||||
|
|
||||||
|
assertThat(bytesSkipped).isEqualTo(1);
|
||||||
|
assertThat(input.getPosition()).isEqualTo(TEST_DATA.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSkipZeroLength() throws Exception {
|
||||||
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
|
|
||||||
|
int bytesRead = input.skip(0);
|
||||||
|
|
||||||
|
assertThat(bytesRead).isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSkipFullyOnce() throws Exception {
|
public void testSkipFullyOnce() throws Exception {
|
||||||
// Skip TEST_DATA.
|
// Skip TEST_DATA.
|
||||||
@ -309,6 +389,86 @@ public class DefaultExtractorInputTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPeekMultipleTimes() throws Exception {
|
||||||
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
|
byte[] target = new byte[TEST_DATA.length];
|
||||||
|
|
||||||
|
// We expect to perform three peeks of three bytes, as setup in buildTestDataSource.
|
||||||
|
int bytesPeeked = 0;
|
||||||
|
bytesPeeked += input.peek(target, 0, TEST_DATA.length);
|
||||||
|
assertThat(bytesPeeked).isEqualTo(3);
|
||||||
|
bytesPeeked += input.peek(target, 3, TEST_DATA.length);
|
||||||
|
assertThat(bytesPeeked).isEqualTo(6);
|
||||||
|
bytesPeeked += input.peek(target, 6, TEST_DATA.length);
|
||||||
|
assertThat(bytesPeeked).isEqualTo(9);
|
||||||
|
assertThat(input.getPeekPosition()).isEqualTo(TEST_DATA.length);
|
||||||
|
assertThat(TEST_DATA).isEqualTo(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPeekAlreadyPeeked() throws Exception {
|
||||||
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
|
byte[] target = new byte[TEST_DATA.length];
|
||||||
|
|
||||||
|
input.advancePeekPosition(TEST_DATA.length);
|
||||||
|
input.resetPeekPosition();
|
||||||
|
int bytesPeeked = input.peek(target, 0, TEST_DATA.length - 1);
|
||||||
|
|
||||||
|
assertThat(bytesPeeked).isEqualTo(TEST_DATA.length - 1);
|
||||||
|
assertThat(input.getPeekPosition()).isEqualTo(TEST_DATA.length - 1);
|
||||||
|
assertThat(Arrays.copyOf(TEST_DATA, TEST_DATA.length - 1))
|
||||||
|
.isEqualTo(Arrays.copyOf(target, TEST_DATA.length - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPeekPartiallyPeeked() throws Exception {
|
||||||
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
|
byte[] target = new byte[TEST_DATA.length];
|
||||||
|
|
||||||
|
input.advancePeekPosition(TEST_DATA.length - 1);
|
||||||
|
input.resetPeekPosition();
|
||||||
|
int bytesPeeked = input.peek(target, 0, TEST_DATA.length);
|
||||||
|
|
||||||
|
assertThat(bytesPeeked).isEqualTo(TEST_DATA.length - 1);
|
||||||
|
assertThat(Arrays.copyOf(TEST_DATA, TEST_DATA.length - 1))
|
||||||
|
.isEqualTo(Arrays.copyOf(target, TEST_DATA.length - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPeekEndOfInputBeforeFirstBytePeeked() throws Exception {
|
||||||
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
|
byte[] target = new byte[TEST_DATA.length];
|
||||||
|
|
||||||
|
input.advancePeekPosition(TEST_DATA.length);
|
||||||
|
int bytesPeeked = input.peek(target, 0, TEST_DATA.length);
|
||||||
|
|
||||||
|
assertThat(bytesPeeked).isEqualTo(RESULT_END_OF_INPUT);
|
||||||
|
assertThat(input.getPeekPosition()).isEqualTo(TEST_DATA.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPeekEndOfInputAfterFirstBytePeeked() throws Exception {
|
||||||
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
|
byte[] target = new byte[TEST_DATA.length];
|
||||||
|
|
||||||
|
input.advancePeekPosition(TEST_DATA.length - 1);
|
||||||
|
int bytesPeeked = input.peek(target, 0, TEST_DATA.length);
|
||||||
|
|
||||||
|
assertThat(bytesPeeked).isEqualTo(1);
|
||||||
|
assertThat(input.getPeekPosition()).isEqualTo(TEST_DATA.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPeekZeroLength() throws Exception {
|
||||||
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
|
byte[] target = new byte[TEST_DATA.length];
|
||||||
|
|
||||||
|
int bytesPeeked = input.peek(target, /* offset= */ 0, /* length= */ 0);
|
||||||
|
|
||||||
|
assertThat(bytesPeeked).isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPeekFully() throws Exception {
|
public void testPeekFully() throws Exception {
|
||||||
DefaultExtractorInput input = createDefaultExtractorInput();
|
DefaultExtractorInput input = createDefaultExtractorInput();
|
||||||
@ -316,14 +476,14 @@ public class DefaultExtractorInputTest {
|
|||||||
input.peekFully(target, 0, TEST_DATA.length);
|
input.peekFully(target, 0, TEST_DATA.length);
|
||||||
|
|
||||||
// Check that we read the whole of TEST_DATA.
|
// Check that we read the whole of TEST_DATA.
|
||||||
assertThat(Arrays.equals(TEST_DATA, target)).isTrue();
|
assertThat(TEST_DATA).isEqualTo(target);
|
||||||
assertThat(input.getPosition()).isEqualTo(0);
|
assertThat(input.getPosition()).isEqualTo(0);
|
||||||
assertThat(input.getPeekPosition()).isEqualTo(TEST_DATA.length);
|
assertThat(input.getPeekPosition()).isEqualTo(TEST_DATA.length);
|
||||||
|
|
||||||
// Check that we can read again from the buffer
|
// Check that we can read again from the buffer
|
||||||
byte[] target2 = new byte[TEST_DATA.length];
|
byte[] target2 = new byte[TEST_DATA.length];
|
||||||
input.readFully(target2, 0, TEST_DATA.length);
|
input.readFully(target2, 0, TEST_DATA.length);
|
||||||
assertThat(Arrays.equals(TEST_DATA, target2)).isTrue();
|
assertThat(TEST_DATA).isEqualTo(target2);
|
||||||
assertThat(input.getPosition()).isEqualTo(TEST_DATA.length);
|
assertThat(input.getPosition()).isEqualTo(TEST_DATA.length);
|
||||||
assertThat(input.getPeekPosition()).isEqualTo(TEST_DATA.length);
|
assertThat(input.getPeekPosition()).isEqualTo(TEST_DATA.length);
|
||||||
|
|
||||||
@ -350,7 +510,7 @@ public class DefaultExtractorInputTest {
|
|||||||
input.peekFully(target, /* offset= */ 0, /* length= */ TEST_DATA.length);
|
input.peekFully(target, /* offset= */ 0, /* length= */ TEST_DATA.length);
|
||||||
|
|
||||||
assertThat(input.getPeekPosition()).isEqualTo(TEST_DATA.length);
|
assertThat(input.getPeekPosition()).isEqualTo(TEST_DATA.length);
|
||||||
assertThat(Arrays.equals(TEST_DATA, Arrays.copyOf(target, TEST_DATA.length))).isTrue();
|
assertThat(TEST_DATA).isEqualTo(Arrays.copyOf(target, TEST_DATA.length));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -360,14 +520,14 @@ public class DefaultExtractorInputTest {
|
|||||||
input.peekFully(target, 0, TEST_DATA.length);
|
input.peekFully(target, 0, TEST_DATA.length);
|
||||||
|
|
||||||
// Check that we read the whole of TEST_DATA.
|
// Check that we read the whole of TEST_DATA.
|
||||||
assertThat(Arrays.equals(TEST_DATA, target)).isTrue();
|
assertThat(TEST_DATA).isEqualTo(target);
|
||||||
assertThat(input.getPosition()).isEqualTo(0);
|
assertThat(input.getPosition()).isEqualTo(0);
|
||||||
|
|
||||||
// Check that we can peek again after resetting.
|
// Check that we can peek again after resetting.
|
||||||
input.resetPeekPosition();
|
input.resetPeekPosition();
|
||||||
byte[] target2 = new byte[TEST_DATA.length];
|
byte[] target2 = new byte[TEST_DATA.length];
|
||||||
input.peekFully(target2, 0, TEST_DATA.length);
|
input.peekFully(target2, 0, TEST_DATA.length);
|
||||||
assertThat(Arrays.equals(TEST_DATA, target2)).isTrue();
|
assertThat(TEST_DATA).isEqualTo(target2);
|
||||||
|
|
||||||
// Check that we fail with EOFException if we peek past the end of the input.
|
// Check that we fail with EOFException if we peek past the end of the input.
|
||||||
try {
|
try {
|
||||||
|
@ -65,7 +65,8 @@ public final class FakeExtractorInput implements ExtractorInput {
|
|||||||
private int readPosition;
|
private int readPosition;
|
||||||
private int peekPosition;
|
private int peekPosition;
|
||||||
|
|
||||||
private final SparseBooleanArray partiallySatisfiedTargetPositions;
|
private final SparseBooleanArray partiallySatisfiedTargetReadPositions;
|
||||||
|
private final SparseBooleanArray partiallySatisfiedTargetPeekPositions;
|
||||||
private final SparseBooleanArray failedReadPositions;
|
private final SparseBooleanArray failedReadPositions;
|
||||||
private final SparseBooleanArray failedPeekPositions;
|
private final SparseBooleanArray failedPeekPositions;
|
||||||
|
|
||||||
@ -75,7 +76,8 @@ public final class FakeExtractorInput implements ExtractorInput {
|
|||||||
this.simulateUnknownLength = simulateUnknownLength;
|
this.simulateUnknownLength = simulateUnknownLength;
|
||||||
this.simulatePartialReads = simulatePartialReads;
|
this.simulatePartialReads = simulatePartialReads;
|
||||||
this.simulateIOErrors = simulateIOErrors;
|
this.simulateIOErrors = simulateIOErrors;
|
||||||
partiallySatisfiedTargetPositions = new SparseBooleanArray();
|
partiallySatisfiedTargetReadPositions = new SparseBooleanArray();
|
||||||
|
partiallySatisfiedTargetPeekPositions = new SparseBooleanArray();
|
||||||
failedReadPositions = new SparseBooleanArray();
|
failedReadPositions = new SparseBooleanArray();
|
||||||
failedPeekPositions = new SparseBooleanArray();
|
failedPeekPositions = new SparseBooleanArray();
|
||||||
}
|
}
|
||||||
@ -84,7 +86,8 @@ public final class FakeExtractorInput implements ExtractorInput {
|
|||||||
public void reset() {
|
public void reset() {
|
||||||
readPosition = 0;
|
readPosition = 0;
|
||||||
peekPosition = 0;
|
peekPosition = 0;
|
||||||
partiallySatisfiedTargetPositions.clear();
|
partiallySatisfiedTargetReadPositions.clear();
|
||||||
|
partiallySatisfiedTargetPeekPositions.clear();
|
||||||
failedReadPositions.clear();
|
failedReadPositions.clear();
|
||||||
failedPeekPositions.clear();
|
failedPeekPositions.clear();
|
||||||
}
|
}
|
||||||
@ -104,7 +107,7 @@ public final class FakeExtractorInput implements ExtractorInput {
|
|||||||
@Override
|
@Override
|
||||||
public int read(byte[] target, int offset, int length) throws IOException {
|
public int read(byte[] target, int offset, int length) throws IOException {
|
||||||
checkIOException(readPosition, failedReadPositions);
|
checkIOException(readPosition, failedReadPositions);
|
||||||
length = getReadLength(length);
|
length = getLengthToRead(readPosition, length, partiallySatisfiedTargetReadPositions);
|
||||||
return readFullyInternal(target, offset, length, true) ? length : C.RESULT_END_OF_INPUT;
|
return readFullyInternal(target, offset, length, true) ? length : C.RESULT_END_OF_INPUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +126,7 @@ public final class FakeExtractorInput implements ExtractorInput {
|
|||||||
@Override
|
@Override
|
||||||
public int skip(int length) throws IOException {
|
public int skip(int length) throws IOException {
|
||||||
checkIOException(readPosition, failedReadPositions);
|
checkIOException(readPosition, failedReadPositions);
|
||||||
length = getReadLength(length);
|
length = getLengthToRead(readPosition, length, partiallySatisfiedTargetReadPositions);
|
||||||
return skipFullyInternal(length, true) ? length : C.RESULT_END_OF_INPUT;
|
return skipFullyInternal(length, true) ? length : C.RESULT_END_OF_INPUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,16 +141,18 @@ public final class FakeExtractorInput implements ExtractorInput {
|
|||||||
skipFully(length, false);
|
skipFully(length, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int peek(byte[] target, int offset, int length) throws IOException {
|
||||||
|
checkIOException(peekPosition, failedPeekPositions);
|
||||||
|
length = getLengthToRead(peekPosition, length, partiallySatisfiedTargetPeekPositions);
|
||||||
|
return peekFullyInternal(target, offset, length, true) ? length : C.RESULT_END_OF_INPUT;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean peekFully(byte[] target, int offset, int length, boolean allowEndOfInput)
|
public boolean peekFully(byte[] target, int offset, int length, boolean allowEndOfInput)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
checkIOException(peekPosition, failedPeekPositions);
|
checkIOException(peekPosition, failedPeekPositions);
|
||||||
if (!checkXFully(allowEndOfInput, peekPosition, length)) {
|
return peekFullyInternal(target, offset, length, allowEndOfInput);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
System.arraycopy(data, peekPosition, target, offset, length);
|
|
||||||
peekPosition += length;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -221,18 +226,19 @@ public final class FakeExtractorInput implements ExtractorInput {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getReadLength(int requestedLength) {
|
private int getLengthToRead(
|
||||||
if (readPosition == data.length) {
|
int position, int requestedLength, SparseBooleanArray partiallySatisfiedTargetPositions) {
|
||||||
|
if (position == data.length) {
|
||||||
// If the requested length is non-zero, the end of the input will be read.
|
// If the requested length is non-zero, the end of the input will be read.
|
||||||
return requestedLength == 0 ? 0 : Integer.MAX_VALUE;
|
return requestedLength == 0 ? 0 : Integer.MAX_VALUE;
|
||||||
}
|
}
|
||||||
int targetPosition = readPosition + requestedLength;
|
int targetPosition = position + requestedLength;
|
||||||
if (simulatePartialReads && requestedLength > 1
|
if (simulatePartialReads && requestedLength > 1
|
||||||
&& !partiallySatisfiedTargetPositions.get(targetPosition)) {
|
&& !partiallySatisfiedTargetPositions.get(targetPosition)) {
|
||||||
partiallySatisfiedTargetPositions.put(targetPosition, true);
|
partiallySatisfiedTargetPositions.put(targetPosition, true);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return Math.min(requestedLength, data.length - readPosition);
|
return Math.min(requestedLength, data.length - position);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean readFullyInternal(byte[] target, int offset, int length, boolean allowEndOfInput)
|
private boolean readFullyInternal(byte[] target, int offset, int length, boolean allowEndOfInput)
|
||||||
@ -255,6 +261,16 @@ public final class FakeExtractorInput implements ExtractorInput {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean peekFullyInternal(byte[] target, int offset, int length, boolean allowEndOfInput)
|
||||||
|
throws EOFException {
|
||||||
|
if (!checkXFully(allowEndOfInput, peekPosition, length)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
System.arraycopy(data, peekPosition, target, offset, length);
|
||||||
|
peekPosition += length;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder of {@link FakeExtractorInput} instances.
|
* Builder of {@link FakeExtractorInput} instances.
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user