Fix paging issue where DataStatus was not updated on insert.
This commit is contained in:
parent
54eb579558
commit
e7a370a549
3 changed files with 125 additions and 31 deletions
|
@ -13,7 +13,8 @@ class DataStatus {
|
|||
private static final Pools.Pool<BitSet> POOL = new Pools.SynchronizedPool<>(1);
|
||||
|
||||
private final BitSet state;
|
||||
private final int size;
|
||||
|
||||
private int size;
|
||||
|
||||
public static DataStatus obtain(int size) {
|
||||
BitSet bitset = POOL.acquire();
|
||||
|
@ -31,6 +32,10 @@ class DataStatus {
|
|||
this.state = bitset;
|
||||
}
|
||||
|
||||
void mark(int position) {
|
||||
state.set(position, true);
|
||||
}
|
||||
|
||||
void markRange(int startInclusive, int endExclusive) {
|
||||
state.set(startInclusive, endExclusive, true);
|
||||
}
|
||||
|
@ -53,6 +58,24 @@ class DataStatus {
|
|||
return -1;
|
||||
}
|
||||
|
||||
boolean get(int position) {
|
||||
return state.get(position);
|
||||
}
|
||||
|
||||
void insertState(int position, boolean value) {
|
||||
if (position < 0 || position > size + 1) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
for (int i = size; i > position; i--) {
|
||||
state.set(i, state.get(i - 1));
|
||||
}
|
||||
|
||||
state.set(position, value);
|
||||
|
||||
this.size = size + 1;
|
||||
}
|
||||
|
||||
int size() {
|
||||
return size;
|
||||
}
|
||||
|
|
|
@ -62,38 +62,43 @@ class FixedSizePagingController<Key, Data> implements PagingController<Key> {
|
|||
return;
|
||||
}
|
||||
|
||||
if (loadState.size() == 0) {
|
||||
liveData.postValue(Collections.emptyList());
|
||||
return;
|
||||
final int loadStart;
|
||||
final int loadEnd;
|
||||
|
||||
synchronized (loadState) {
|
||||
if (loadState.size() == 0) {
|
||||
liveData.postValue(Collections.emptyList());
|
||||
return;
|
||||
}
|
||||
|
||||
int leftPageBoundary = (aroundIndex / config.pageSize()) * config.pageSize();
|
||||
int rightPageBoundary = leftPageBoundary + config.pageSize();
|
||||
int buffer = config.bufferPages() * config.pageSize();
|
||||
|
||||
int leftLoadBoundary = Math.max(0, leftPageBoundary - buffer);
|
||||
int rightLoadBoundary = Math.min(loadState.size(), rightPageBoundary + buffer);
|
||||
|
||||
loadStart = loadState.getEarliestUnmarkedIndexInRange(leftLoadBoundary, rightLoadBoundary);
|
||||
|
||||
if (loadStart < 0) {
|
||||
if (DEBUG) Log.i(TAG, buildLog(aroundIndex, "loadStart < 0"));
|
||||
return;
|
||||
}
|
||||
|
||||
loadEnd = loadState.getLatestUnmarkedIndexInRange(Math.max(leftLoadBoundary, loadStart), rightLoadBoundary) + 1;
|
||||
|
||||
if (loadEnd <= loadStart) {
|
||||
if (DEBUG) Log.i(TAG, buildLog(aroundIndex, "loadEnd <= loadStart, loadEnd: " + loadEnd + ", loadStart: " + loadStart));
|
||||
return;
|
||||
}
|
||||
|
||||
int totalSize = loadState.size();
|
||||
|
||||
loadState.markRange(loadStart, loadEnd);
|
||||
|
||||
if (DEBUG) Log.i(TAG, buildLog(aroundIndex, "start: " + loadStart + ", end: " + loadEnd + ", totalSize: " + totalSize));
|
||||
}
|
||||
|
||||
int leftPageBoundary = (aroundIndex / config.pageSize()) * config.pageSize();
|
||||
int rightPageBoundary = leftPageBoundary + config.pageSize();
|
||||
int buffer = config.bufferPages() * config.pageSize();
|
||||
|
||||
int leftLoadBoundary = Math.max(0, leftPageBoundary - buffer);
|
||||
int rightLoadBoundary = Math.min(loadState.size(), rightPageBoundary + buffer);
|
||||
|
||||
int loadStart = loadState.getEarliestUnmarkedIndexInRange(leftLoadBoundary, rightLoadBoundary);
|
||||
|
||||
if (loadStart < 0) {
|
||||
if (DEBUG) Log.i(TAG, buildLog(aroundIndex, "loadStart < 0"));
|
||||
return;
|
||||
}
|
||||
|
||||
int loadEnd = loadState.getLatestUnmarkedIndexInRange(Math.max(leftLoadBoundary, loadStart), rightLoadBoundary) + 1;
|
||||
|
||||
if (loadEnd <= loadStart) {
|
||||
if (DEBUG) Log.i(TAG, buildLog(aroundIndex, "loadEnd <= loadStart, loadEnd: " + loadEnd + ", loadStart: " + loadStart));
|
||||
return;
|
||||
}
|
||||
|
||||
int totalSize = loadState.size();
|
||||
|
||||
loadState.markRange(loadStart, loadEnd);
|
||||
|
||||
if (DEBUG) Log.i(TAG, buildLog(aroundIndex, "start: " + loadStart + ", end: " + loadEnd + ", totalSize: " + totalSize));
|
||||
|
||||
FETCH_EXECUTOR.execute(() -> {
|
||||
if (invalidated) {
|
||||
Log.w(TAG, buildLog(aroundIndex, "Invalidated! At beginning of load task."));
|
||||
|
@ -147,6 +152,10 @@ class FixedSizePagingController<Key, Data> implements PagingController<Key> {
|
|||
return;
|
||||
}
|
||||
|
||||
synchronized (loadState) {
|
||||
loadState.mark(position);
|
||||
}
|
||||
|
||||
Data item = dataSource.load(key);
|
||||
|
||||
if (item == null) {
|
||||
|
@ -180,6 +189,10 @@ class FixedSizePagingController<Key, Data> implements PagingController<Key> {
|
|||
return;
|
||||
}
|
||||
|
||||
synchronized (loadState) {
|
||||
loadState.insertState(position, true);
|
||||
}
|
||||
|
||||
Data item = dataSource.load(key);
|
||||
|
||||
if (item == null) {
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package org.signal.paging;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class DataStatusTest {
|
||||
|
||||
@Test
|
||||
public void insertState_initiallyEmpty_InsertAtZero() {
|
||||
DataStatus subject = DataStatus.obtain(0);
|
||||
subject.insertState(0, true);
|
||||
|
||||
assertEquals(1, subject.size());
|
||||
assertTrue(subject.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertState_someData_InsertAtZero() {
|
||||
DataStatus subject = DataStatus.obtain(2);
|
||||
subject.mark(1);
|
||||
|
||||
subject.insertState(0, true);
|
||||
|
||||
assertEquals(3, subject.size());
|
||||
assertTrue(subject.get(0));
|
||||
assertFalse(subject.get(1));
|
||||
assertTrue(subject.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertState_someData_InsertAtOne() {
|
||||
DataStatus subject = DataStatus.obtain(3);
|
||||
subject.mark(1);
|
||||
|
||||
subject.insertState(1, true);
|
||||
|
||||
assertEquals(4, subject.size());
|
||||
assertFalse(subject.get(0));
|
||||
assertTrue(subject.get(1));
|
||||
assertTrue(subject.get(2));
|
||||
assertFalse(subject.get(3));
|
||||
}
|
||||
|
||||
@Test(expected = IndexOutOfBoundsException.class)
|
||||
public void insertState_negativeThrows() {
|
||||
DataStatus subject = DataStatus.obtain(0);
|
||||
subject.insertState(-1, true);
|
||||
}
|
||||
|
||||
@Test(expected = IndexOutOfBoundsException.class)
|
||||
public void insertState_largerThanSizePlusOneThrows() {
|
||||
DataStatus subject = DataStatus.obtain(0);
|
||||
subject.insertState(2, true);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue