Support removal of ranges from ShuffleOrders

This allows more efficient range removals and is consistent with addition,
which supports adding multiple elements in a single operation.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=222396310
This commit is contained in:
aquilescanta 2018-11-21 05:58:24 -08:00 committed by Oliver Woodman
parent 265462bcbd
commit 5f12b065a4
4 changed files with 65 additions and 52 deletions

View File

@ -570,9 +570,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
if (fromIndex == 0 && toIndex == shuffleOrder.getLength()) { if (fromIndex == 0 && toIndex == shuffleOrder.getLength()) {
shuffleOrder = shuffleOrder.cloneAndClear(); shuffleOrder = shuffleOrder.cloneAndClear();
} else { } else {
for (int index = toIndex - 1; index >= fromIndex; index--) { shuffleOrder = shuffleOrder.cloneAndRemove(fromIndex, toIndex);
shuffleOrder = shuffleOrder.cloneAndRemove(index);
}
} }
for (int index = toIndex - 1; index >= fromIndex; index--) { for (int index = toIndex - 1; index >= fromIndex; index--) {
removeMediaSourceInternal(index); removeMediaSourceInternal(index);
@ -581,7 +579,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
break; break;
case MSG_MOVE: case MSG_MOVE:
MessageData<Integer> moveMessage = (MessageData<Integer>) Util.castNonNull(message); MessageData<Integer> moveMessage = (MessageData<Integer>) Util.castNonNull(message);
shuffleOrder = shuffleOrder.cloneAndRemove(moveMessage.index); shuffleOrder = shuffleOrder.cloneAndRemove(moveMessage.index, moveMessage.index + 1);
shuffleOrder = shuffleOrder.cloneAndInsert(moveMessage.customData, 1); shuffleOrder = shuffleOrder.cloneAndInsert(moveMessage.customData, 1);
moveMediaSourceInternal(moveMessage.index, moveMessage.customData); moveMediaSourceInternal(moveMessage.index, moveMessage.customData);
scheduleListenerNotification(moveMessage.actionOnCompletion); scheduleListenerNotification(moveMessage.actionOnCompletion);

View File

@ -135,15 +135,16 @@ public interface ShuffleOrder {
} }
@Override @Override
public ShuffleOrder cloneAndRemove(int removalIndex) { public ShuffleOrder cloneAndRemove(int indexFrom, int indexToExclusive) {
int[] newShuffled = new int[shuffled.length - 1]; int numberOfElementsToRemove = indexToExclusive - indexFrom;
boolean foundRemovedElement = false; int[] newShuffled = new int[shuffled.length - numberOfElementsToRemove];
int foundElementsCount = 0;
for (int i = 0; i < shuffled.length; i++) { for (int i = 0; i < shuffled.length; i++) {
if (shuffled[i] == removalIndex) { if (shuffled[i] >= indexFrom && shuffled[i] < indexToExclusive) {
foundRemovedElement = true; foundElementsCount++;
} else { } else {
newShuffled[foundRemovedElement ? i - 1 : i] = shuffled[i] > removalIndex newShuffled[i - foundElementsCount] =
? shuffled[i] - 1 : shuffled[i]; shuffled[i] >= indexFrom ? shuffled[i] - numberOfElementsToRemove : shuffled[i];
} }
} }
return new DefaultShuffleOrder(newShuffled, new Random(random.nextLong())); return new DefaultShuffleOrder(newShuffled, new Random(random.nextLong()));
@ -213,8 +214,8 @@ public interface ShuffleOrder {
} }
@Override @Override
public ShuffleOrder cloneAndRemove(int removalIndex) { public ShuffleOrder cloneAndRemove(int indexFrom, int indexToExclusive) {
return new UnshuffledShuffleOrder(length - 1); return new UnshuffledShuffleOrder(length - indexToExclusive + indexFrom);
} }
@Override @Override
@ -268,12 +269,14 @@ public interface ShuffleOrder {
ShuffleOrder cloneAndInsert(int insertionIndex, int insertionCount); ShuffleOrder cloneAndInsert(int insertionIndex, int insertionCount);
/** /**
* Returns a copy of the shuffle order with one element removed. * Returns a copy of the shuffle order with a range of elements removed.
* *
* @param removalIndex The index of the element in the unshuffled order which is to be removed. * @param indexFrom The starting index in the unshuffled order of the range to remove.
* @return A copy of this {@link ShuffleOrder} without the removed element. * @param indexToExclusive The smallest index (must be greater or equal to {@code indexFrom}) that
* will not be removed.
* @return A copy of this {@link ShuffleOrder} without the elements in the removed range.
*/ */
ShuffleOrder cloneAndRemove(int removalIndex); ShuffleOrder cloneAndRemove(int indexFrom, int indexToExclusive);
/** Returns a copy of the shuffle order with all elements removed. */ /** Returns a copy of the shuffle order with all elements removed. */
ShuffleOrder cloneAndClear(); ShuffleOrder cloneAndClear();

View File

@ -45,10 +45,32 @@ public final class ShuffleOrderTest {
testCloneAndInsert(new DefaultShuffleOrder(initialLength, RANDOM_SEED), insertionPoint, 5); testCloneAndInsert(new DefaultShuffleOrder(initialLength, RANDOM_SEED), insertionPoint, 5);
} }
} }
testCloneAndRemove(new DefaultShuffleOrder(5, RANDOM_SEED), 0); testCloneAndRemove(new DefaultShuffleOrder(5, RANDOM_SEED), 0, 1);
testCloneAndRemove(new DefaultShuffleOrder(5, RANDOM_SEED), 2); testCloneAndRemove(new DefaultShuffleOrder(5, RANDOM_SEED), 2, 3);
testCloneAndRemove(new DefaultShuffleOrder(5, RANDOM_SEED), 4); testCloneAndRemove(new DefaultShuffleOrder(5, RANDOM_SEED), 4, 5);
testCloneAndRemove(new DefaultShuffleOrder(1, RANDOM_SEED), 0); testCloneAndRemove(new DefaultShuffleOrder(1, RANDOM_SEED), 0, 1);
testCloneAndRemove(new DefaultShuffleOrder(1000, RANDOM_SEED), 0, 1000);
testCloneAndRemove(new DefaultShuffleOrder(1000, RANDOM_SEED), 0, 999);
testCloneAndRemove(new DefaultShuffleOrder(1000, RANDOM_SEED), 0, 500);
testCloneAndRemove(new DefaultShuffleOrder(1000, RANDOM_SEED), 100, 600);
testCloneAndRemove(new DefaultShuffleOrder(1000, RANDOM_SEED), 500, 1000);
}
@Test
public void testDefaultShuffleOrderSideloaded() {
int[] shuffledIndices = new int[] {2, 1, 0, 4, 3};
ShuffleOrder shuffleOrder = new DefaultShuffleOrder(shuffledIndices, RANDOM_SEED);
assertThat(shuffleOrder.getFirstIndex()).isEqualTo(2);
assertThat(shuffleOrder.getLastIndex()).isEqualTo(3);
for (int i = 0; i < 4; i++) {
assertThat(shuffleOrder.getNextIndex(shuffledIndices[i])).isEqualTo(shuffledIndices[i + 1]);
}
assertThat(shuffleOrder.getNextIndex(3)).isEqualTo(C.INDEX_UNSET);
for (int i = 4; i > 0; i--) {
assertThat(shuffleOrder.getPreviousIndex(shuffledIndices[i]))
.isEqualTo(shuffledIndices[i - 1]);
}
assertThat(shuffleOrder.getPreviousIndex(2)).isEqualTo(C.INDEX_UNSET);
} }
@Test @Test
@ -63,10 +85,15 @@ public final class ShuffleOrderTest {
testCloneAndInsert(new UnshuffledShuffleOrder(initialLength), insertionPoint, 5); testCloneAndInsert(new UnshuffledShuffleOrder(initialLength), insertionPoint, 5);
} }
} }
testCloneAndRemove(new UnshuffledShuffleOrder(5), 0); testCloneAndRemove(new UnshuffledShuffleOrder(5), 0, 1);
testCloneAndRemove(new UnshuffledShuffleOrder(5), 2); testCloneAndRemove(new UnshuffledShuffleOrder(5), 2, 3);
testCloneAndRemove(new UnshuffledShuffleOrder(5), 4); testCloneAndRemove(new UnshuffledShuffleOrder(5), 4, 5);
testCloneAndRemove(new UnshuffledShuffleOrder(1), 0); testCloneAndRemove(new UnshuffledShuffleOrder(1), 0, 1);
testCloneAndRemove(new UnshuffledShuffleOrder(1000), 0, 1000);
testCloneAndRemove(new UnshuffledShuffleOrder(1000), 0, 999);
testCloneAndRemove(new UnshuffledShuffleOrder(1000), 0, 500);
testCloneAndRemove(new UnshuffledShuffleOrder(1000), 100, 600);
testCloneAndRemove(new UnshuffledShuffleOrder(1000), 500, 1000);
} }
@Test @Test
@ -79,23 +106,6 @@ public final class ShuffleOrderTest {
} }
} }
@Test
public void testSideloadedShuffleOrder() {
int[] shuffledIndices = new int[] {2, 1, 0, 4, 3};
ShuffleOrder shuffleOrder = new DefaultShuffleOrder(shuffledIndices, RANDOM_SEED);
assertThat(shuffleOrder.getFirstIndex()).isEqualTo(2);
assertThat(shuffleOrder.getLastIndex()).isEqualTo(3);
for (int i = 0; i < 4; i++) {
assertThat(shuffleOrder.getNextIndex(shuffledIndices[i])).isEqualTo(shuffledIndices[i + 1]);
}
assertThat(shuffleOrder.getNextIndex(3)).isEqualTo(C.INDEX_UNSET);
for (int i = 4; i > 0; i--) {
assertThat(shuffleOrder.getPreviousIndex(shuffledIndices[i]))
.isEqualTo(shuffledIndices[i - 1]);
}
assertThat(shuffleOrder.getPreviousIndex(2)).isEqualTo(C.INDEX_UNSET);
}
private static void assertShuffleOrderCorrectness(ShuffleOrder shuffleOrder, int length) { private static void assertShuffleOrderCorrectness(ShuffleOrder shuffleOrder, int length) {
assertThat(shuffleOrder.getLength()).isEqualTo(length); assertThat(shuffleOrder.getLength()).isEqualTo(length);
if (length == 0) { if (length == 0) {
@ -137,22 +147,24 @@ public final class ShuffleOrderTest {
} }
} }
private static void testCloneAndRemove(ShuffleOrder shuffleOrder, int position) { private static void testCloneAndRemove(
ShuffleOrder newOrder = shuffleOrder.cloneAndRemove(position); ShuffleOrder shuffleOrder, int indexFrom, int indexToExclusive) {
assertShuffleOrderCorrectness(newOrder, shuffleOrder.getLength() - 1); int numberOfElementsToRemove = indexToExclusive - indexFrom;
ShuffleOrder newOrder = shuffleOrder.cloneAndRemove(indexFrom, indexToExclusive);
assertShuffleOrderCorrectness(newOrder, shuffleOrder.getLength() - numberOfElementsToRemove);
// Assert all elements still have the relative same order // Assert all elements still have the relative same order
for (int i = 0; i < shuffleOrder.getLength(); i++) { for (int i = 0; i < shuffleOrder.getLength(); i++) {
if (i == position) { if (i >= indexFrom && i < indexToExclusive) {
continue; continue;
} }
int expectedNextIndex = shuffleOrder.getNextIndex(i); int expectedNextIndex = shuffleOrder.getNextIndex(i);
if (expectedNextIndex == position) { while (expectedNextIndex >= indexFrom && expectedNextIndex < indexToExclusive) {
expectedNextIndex = shuffleOrder.getNextIndex(expectedNextIndex); expectedNextIndex = shuffleOrder.getNextIndex(expectedNextIndex);
} }
if (expectedNextIndex != C.INDEX_UNSET && expectedNextIndex >= position) { if (expectedNextIndex != C.INDEX_UNSET && expectedNextIndex >= indexFrom) {
expectedNextIndex--; expectedNextIndex -= numberOfElementsToRemove;
} }
int newNextIndex = newOrder.getNextIndex(i < position ? i : i - 1); int newNextIndex = newOrder.getNextIndex(i < indexFrom ? i : i - numberOfElementsToRemove);
assertThat(newNextIndex).isEqualTo(expectedNextIndex); assertThat(newNextIndex).isEqualTo(expectedNextIndex);
} }
} }

View File

@ -61,8 +61,8 @@ public final class FakeShuffleOrder implements ShuffleOrder {
} }
@Override @Override
public ShuffleOrder cloneAndRemove(int removalIndex) { public ShuffleOrder cloneAndRemove(int indexFrom, int indexToExclusive) {
return new FakeShuffleOrder(length - 1); return new FakeShuffleOrder(length - indexToExclusive + indexFrom);
} }
@Override @Override