Advance SampleQueue writeAllocationNode earlier

Previously, writeAllocationNode was not advanced to the
terminating node when finishing writing sample data that
fills exactly up to the end of the current write node.
This wasn't actually broken, but is confusing because it
causes edge cases where the start/read references could
temporarily refer the node after the current write node.
This change advances the write reference in this case,
removing this confusion and bringing the implementation
in line with what the design doc says happens.

Also making some simplification and consistency changes.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=159099522
This commit is contained in:
olly 2017-06-15 07:06:38 -07:00 committed by Oliver Woodman
parent a913fd952f
commit 2a353a834d
2 changed files with 56 additions and 50 deletions

View File

@ -75,7 +75,6 @@ public final class SampleQueue implements TrackOutput {
private Format lastUnadjustedFormat; private Format lastUnadjustedFormat;
private long sampleOffsetUs; private long sampleOffsetUs;
private long totalBytesWritten; private long totalBytesWritten;
private int lastAllocationOffset;
private boolean pendingSplice; private boolean pendingSplice;
private UpstreamFormatChangedListener upstreamFormatChangeListener; private UpstreamFormatChangedListener upstreamFormatChangeListener;
@ -89,7 +88,6 @@ public final class SampleQueue implements TrackOutput {
extrasHolder = new SampleExtrasHolder(); extrasHolder = new SampleExtrasHolder();
scratch = new ParsableByteArray(INITIAL_SCRATCH_SIZE); scratch = new ParsableByteArray(INITIAL_SCRATCH_SIZE);
state = new AtomicInteger(); state = new AtomicInteger();
lastAllocationOffset = allocationLength;
firstAllocationNode = new AllocationNode(0, allocationLength); firstAllocationNode = new AllocationNode(0, allocationLength);
writeAllocationNode = firstAllocationNode; writeAllocationNode = firstAllocationNode;
} }
@ -166,7 +164,6 @@ public final class SampleQueue implements TrackOutput {
writeAllocationNode = newWriteAllocationNode; writeAllocationNode = newWriteAllocationNode;
writeAllocationNode.next = new AllocationNode(writeAllocationNode.endPosition, writeAllocationNode.next = new AllocationNode(writeAllocationNode.endPosition,
allocationLength); allocationLength);
lastAllocationOffset = (int) (absolutePosition - writeAllocationNode.startPosition);
} }
} }
@ -388,12 +385,11 @@ public final class SampleQueue implements TrackOutput {
int remaining = length; int remaining = length;
dropDownstreamTo(absolutePosition); dropDownstreamTo(absolutePosition);
while (remaining > 0) { while (remaining > 0) {
int positionInAllocation = (int) (absolutePosition - firstAllocationNode.startPosition); int toCopy = Math.min(remaining, (int) (firstAllocationNode.endPosition - absolutePosition));
int toCopy = Math.min(remaining, allocationLength - positionInAllocation);
Allocation allocation = firstAllocationNode.allocation; Allocation allocation = firstAllocationNode.allocation;
target.put(allocation.data, allocation.translateOffset(positionInAllocation), toCopy); target.put(allocation.data, firstAllocationNode.translateOffset(absolutePosition), toCopy);
absolutePosition += toCopy;
remaining -= toCopy; remaining -= toCopy;
absolutePosition += toCopy;
if (absolutePosition == firstAllocationNode.endPosition) { if (absolutePosition == firstAllocationNode.endPosition) {
allocator.release(allocation); allocator.release(allocation);
firstAllocationNode = firstAllocationNode.clear(); firstAllocationNode = firstAllocationNode.clear();
@ -409,16 +405,15 @@ public final class SampleQueue implements TrackOutput {
* @param length The number of bytes to read. * @param length The number of bytes to read.
*/ */
private void readData(long absolutePosition, byte[] target, int length) { private void readData(long absolutePosition, byte[] target, int length) {
int bytesRead = 0; int remaining = length;
dropDownstreamTo(absolutePosition); dropDownstreamTo(absolutePosition);
while (bytesRead < length) { while (remaining > 0) {
int positionInAllocation = (int) (absolutePosition - firstAllocationNode.startPosition); int toCopy = Math.min(remaining, (int) (firstAllocationNode.endPosition - absolutePosition));
int toCopy = Math.min(length - bytesRead, allocationLength - positionInAllocation);
Allocation allocation = firstAllocationNode.allocation; Allocation allocation = firstAllocationNode.allocation;
System.arraycopy(allocation.data, allocation.translateOffset(positionInAllocation), target, System.arraycopy(allocation.data, firstAllocationNode.translateOffset(absolutePosition),
bytesRead, toCopy); target, length - remaining, toCopy);
remaining -= toCopy;
absolutePosition += toCopy; absolutePosition += toCopy;
bytesRead += toCopy;
if (absolutePosition == firstAllocationNode.endPosition) { if (absolutePosition == firstAllocationNode.endPosition) {
allocator.release(allocation); allocator.release(allocation);
firstAllocationNode = firstAllocationNode.clear(); firstAllocationNode = firstAllocationNode.clear();
@ -488,18 +483,16 @@ public final class SampleQueue implements TrackOutput {
return bytesSkipped; return bytesSkipped;
} }
try { try {
length = prepareForAppend(length); length = preAppend(length);
Allocation writeAllocation = writeAllocationNode.allocation; int bytesAppended = input.read(writeAllocationNode.allocation.data,
int bytesAppended = input.read(writeAllocation.data, writeAllocationNode.translateOffset(totalBytesWritten), length);
writeAllocation.translateOffset(lastAllocationOffset), length);
if (bytesAppended == C.RESULT_END_OF_INPUT) { if (bytesAppended == C.RESULT_END_OF_INPUT) {
if (allowEndOfInput) { if (allowEndOfInput) {
return C.RESULT_END_OF_INPUT; return C.RESULT_END_OF_INPUT;
} }
throw new EOFException(); throw new EOFException();
} }
lastAllocationOffset += bytesAppended; postAppend(bytesAppended);
totalBytesWritten += bytesAppended;
return bytesAppended; return bytesAppended;
} finally { } finally {
endWriteOperation(); endWriteOperation();
@ -513,13 +506,11 @@ public final class SampleQueue implements TrackOutput {
return; return;
} }
while (length > 0) { while (length > 0) {
int thisAppendLength = prepareForAppend(length); int bytesAppended = preAppend(length);
Allocation writeAllocation = writeAllocationNode.allocation; buffer.readBytes(writeAllocationNode.allocation.data,
buffer.readBytes(writeAllocation.data, writeAllocation.translateOffset(lastAllocationOffset), writeAllocationNode.translateOffset(totalBytesWritten), bytesAppended);
thisAppendLength); length -= bytesAppended;
lastAllocationOffset += thisAppendLength; postAppend(bytesAppended);
totalBytesWritten += thisAppendLength;
length -= thisAppendLength;
} }
endWriteOperation(); endWriteOperation();
} }
@ -567,7 +558,6 @@ public final class SampleQueue implements TrackOutput {
firstAllocationNode = new AllocationNode(0, allocationLength); firstAllocationNode = new AllocationNode(0, allocationLength);
writeAllocationNode = firstAllocationNode; writeAllocationNode = firstAllocationNode;
totalBytesWritten = 0; totalBytesWritten = 0;
lastAllocationOffset = allocationLength;
allocator.trim(); allocator.trim();
} }
@ -595,19 +585,31 @@ public final class SampleQueue implements TrackOutput {
} }
/** /**
* Prepares the rolling sample buffer for an append of up to {@code length} bytes, returning the * Called before writing sample data to {@link #writeAllocationNode}. May cause
* number of bytes that can actually be appended. * {@link #writeAllocationNode} to be initialized.
*
* @param length The number of bytes that the caller wishes to write.
* @return The number of bytes that the caller is permitted to write, which may be less than
* {@code length}.
*/ */
private int prepareForAppend(int length) { private int preAppend(int length) {
if (lastAllocationOffset == allocationLength) { if (!writeAllocationNode.wasInitialized) {
lastAllocationOffset = 0;
if (writeAllocationNode.wasInitialized) {
writeAllocationNode = writeAllocationNode.next;
}
writeAllocationNode.initialize(allocator.allocate(), writeAllocationNode.initialize(allocator.allocate(),
new AllocationNode(writeAllocationNode.endPosition, allocationLength)); new AllocationNode(writeAllocationNode.endPosition, allocationLength));
} }
return Math.min(length, allocationLength - lastAllocationOffset); return Math.min(length, (int) (writeAllocationNode.endPosition - totalBytesWritten));
}
/**
* Called after writing sample data. May cause {@link #writeAllocationNode} to be advanced.
*
* @param length The number of bytes that were written.
*/
private void postAppend(int length) {
totalBytesWritten += length;
if (totalBytesWritten == writeAllocationNode.endPosition) {
writeAllocationNode = writeAllocationNode.next;
}
} }
/** /**
@ -676,6 +678,17 @@ public final class SampleQueue implements TrackOutput {
wasInitialized = true; wasInitialized = true;
} }
/**
* Gets the offset into the {@link #allocation}'s {@link Allocation#data} that corresponds to
* the specified absolute position.
*
* @param absolutePosition The absolute position.
* @return The corresponding offset into the allocation's data.
*/
public int translateOffset(long absolutePosition) {
return (int) (absolutePosition - startPosition) + allocation.offset;
}
/** /**
* Clears {@link #allocation}. * Clears {@link #allocation}.
* *

View File

@ -25,29 +25,22 @@ public final class Allocation {
/** /**
* The array containing the allocated space. The allocated space might not be at the start of the * The array containing the allocated space. The allocated space might not be at the start of the
* array, and so {@link #translateOffset(int)} method must be used when indexing into it. * array, and so {@link #offset} must be used when indexing into it.
*/ */
public final byte[] data; public final byte[] data;
private final int offset; /**
* The offset of the allocated space in {@link #data}.
*/
public final int offset;
/** /**
* @param data The array containing the allocated space. * @param data The array containing the allocated space.
* @param offset The offset of the allocated space within the array. * @param offset The offset of the allocated space in {@code data}.
*/ */
public Allocation(byte[] data, int offset) { public Allocation(byte[] data, int offset) {
this.data = data; this.data = data;
this.offset = offset; this.offset = offset;
} }
/**
* Translates a zero-based offset into the allocation to the corresponding {@link #data} offset.
*
* @param offset The zero-based offset to translate.
* @return The corresponding offset in {@link #data}.
*/
public int translateOffset(int offset) {
return this.offset + offset;
}
} }