Commit ff1efd4e by kimvde Committed by Oliver Woodman

Add peek() method to ExtractorInput

PiperOrigin-RevId: 284537150
parent 24afcdc3
...@@ -58,7 +58,9 @@ public final class DefaultExtractorInput implements ExtractorInput { ...@@ -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;
...@@ -111,6 +113,31 @@ public final class DefaultExtractorInput implements ExtractorInput { ...@@ -111,6 +113,31 @@ public final class DefaultExtractorInput implements ExtractorInput {
} }
@Override @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
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 {
if (!advancePeekPosition(length, allowEndOfInput)) { if (!advancePeekPosition(length, allowEndOfInput)) {
...@@ -201,7 +228,7 @@ public final class DefaultExtractorInput implements ExtractorInput { ...@@ -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; ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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.
......
...@@ -65,7 +65,8 @@ public final class FakeExtractorInput implements ExtractorInput { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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;
} }
...@@ -139,15 +142,17 @@ public final class FakeExtractorInput implements ExtractorInput { ...@@ -139,15 +142,17 @@ public final class FakeExtractorInput implements ExtractorInput {
} }
@Override @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
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 { ...@@ -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 { ...@@ -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.
*/ */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment