Add setCapacity and removeRange to WatchedSparseBooleanMatrix

- This CL adds #setCapacity and #removeRange APIs to
  WatchedSparseBooleanMatrix for AppsFilter to update filter
  cache more efficiently when a user is created or deleted.

- Fix a crash in the #pack function when the caller invokes #compat
  to shrink the matrix.

Test: atest WatcherTest
Bug: 187853334
Change-Id: I6a3c87a7492793767f888487381182955d564d62
This commit is contained in:
Rhed Jao
2021-10-13 17:49:36 +08:00
parent 72e972d5c2
commit dc4d343c3c
2 changed files with 122 additions and 5 deletions

View File

@@ -262,6 +262,33 @@ public class WatchedSparseBooleanMatrix extends WatchableImpl implements Snappab
onChanged();
}
/**
* Removes all of the mappings whose index is between {@code fromIndex}, inclusive, and
* {@code toIndex}, exclusive. The matrix does not shrink.
*/
public void removeRange(int fromIndex, int toIndex) {
if (toIndex < fromIndex) {
throw new ArrayIndexOutOfBoundsException("toIndex < fromIndex");
}
final int num = toIndex - fromIndex;
if (num == 0) {
return;
}
validateIndex(fromIndex);
validateIndex(toIndex - 1);
for (int i = fromIndex; i < toIndex; i++) {
mInUse[mMap[i]] = false;
}
System.arraycopy(mKeys, toIndex, mKeys, fromIndex, mSize - toIndex);
System.arraycopy(mMap, toIndex, mMap, fromIndex, mSize - toIndex);
for (int i = mSize - num; i < mSize; i++) {
mKeys[i] = 0;
mMap[i] = 0;
}
mSize -= num;
onChanged();
}
/**
* Returns the number of key-value mappings that this WatchedSparseBooleanMatrix
* currently stores.
@@ -371,7 +398,7 @@ public class WatchedSparseBooleanMatrix extends WatchableImpl implements Snappab
// Preemptively grow the matrix, which also grows the free list.
growMatrix();
}
int newIndex = nextFree();
int newIndex = nextFree(true /* acquire */);
mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
mMap = GrowingArrayUtils.insert(mMap, mSize, i, newIndex);
mSize++;
@@ -447,12 +474,12 @@ public class WatchedSparseBooleanMatrix extends WatchableImpl implements Snappab
}
/**
* Find an unused storage index, mark it in-use, and return it.
* Find an unused storage index, and return it. Mark it in-use if the {@code acquire} is true.
*/
private int nextFree() {
private int nextFree(boolean acquire) {
for (int i = 0; i < mInUse.length; i++) {
if (!mInUse[i]) {
mInUse[i] = true;
mInUse[i] = acquire;
return i;
}
}
@@ -488,7 +515,8 @@ public class WatchedSparseBooleanMatrix extends WatchableImpl implements Snappab
}
// dst and src are identify raw (row, col) in mValues. srcIndex is the index (as
// in the result of keyAt()) of the key being relocated.
for (int dst = nextFree(); dst < mSize; dst = nextFree()) {
for (int dst = nextFree(false); dst < mSize; dst = nextFree(false)) {
mInUse[dst] = true;
int srcIndex = lastInuse();
int src = mMap[srcIndex];
mInUse[src] = false;
@@ -538,6 +566,20 @@ public class WatchedSparseBooleanMatrix extends WatchableImpl implements Snappab
return mOrder;
}
/**
* Set capacity to enlarge the size of the 2D matrix. Capacity less than the {@link #capacity()}
* is not supported.
*/
public void setCapacity(int capacity) {
if (capacity <= mOrder) {
return;
}
if (capacity % STEP != 0) {
capacity = ((capacity / STEP) + 1) * STEP;
}
resizeMatrix(capacity);
}
/**
* {@inheritDoc}
*/

View File

@@ -915,6 +915,23 @@ public class WatcherTest {
}
}
// Fill new cells in the matrix which has enlarged capacity.
private void fillNew(WatchedSparseBooleanMatrix matrix, int initialCapacity,
int newCapacity, int[] indexes) {
final int size = newCapacity;
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (i < initialCapacity && j < initialCapacity) {
// Do not touch old cells
continue;
}
final int row = indexes[i];
final int col = indexes[j];
matrix.put(row, col, cellValue(i, j));
}
}
}
// Verify the content of a matrix. This asserts on mismatch. Selected indices may
// have been deleted.
private void verify(WatchedSparseBooleanMatrix matrix, int[] indexes, boolean[] absent) {
@@ -989,6 +1006,24 @@ public class WatcherTest {
assertTrue("Matrix shrink", finalCapacity - matrix.size() < matrix.STEP);
}
private void matrixSetCapacity(WatchedSparseBooleanMatrix matrix, int newCapacity,
IndexGenerator indexer) {
final int initialCapacity = matrix.capacity();
final int[] indexes = indexer.indexes(Math.max(initialCapacity, newCapacity));
fill(matrix, initialCapacity, indexes);
matrix.setCapacity(newCapacity);
fillNew(matrix, initialCapacity, newCapacity, indexes);
assertEquals(matrix.size(), indexes.length);
verify(matrix, indexes, null);
// Test the keyAt/indexOfKey methods
for (int i = 0; i < matrix.size(); i++) {
int key = indexes[i];
assertEquals(matrix.keyAt(matrix.indexOfKey(key)), key);
}
}
@Test
public void testWatchedSparseBooleanMatrix() {
final String name = "WatchedSparseBooleanMatrix";
@@ -1051,6 +1086,46 @@ public class WatcherTest {
assertEquals(a.equals(s), false);
}
@Test
public void testWatchedSparseBooleanMatrix_setCapacity() {
final IndexGenerator indexer = new IndexGenerator(3);
matrixSetCapacity(new WatchedSparseBooleanMatrix(500), 1000, indexer);
matrixSetCapacity(new WatchedSparseBooleanMatrix(1000), 500, indexer);
}
@Test
public void testWatchedSparseBooleanMatrix_removeRangeAndShrink() {
final IndexGenerator indexer = new IndexGenerator(3);
final int initialCapacity = 500;
final int removeCounts = 33;
final WatchedSparseBooleanMatrix matrix = new WatchedSparseBooleanMatrix(initialCapacity);
final int[] indexes = indexer.indexes(initialCapacity);
final boolean[] absents = new boolean[initialCapacity];
fill(matrix, initialCapacity, indexes);
assertEquals(matrix.size(), initialCapacity);
for (int i = 0; i < initialCapacity / removeCounts; i++) {
final int size = matrix.size();
final int fromIndex = (size / 2 < removeCounts ? 0 : size / 2 - removeCounts);
final int toIndex = (fromIndex + removeCounts > size ? size : fromIndex + removeCounts);
for (int index = fromIndex; index < toIndex; index++) {
final int key = matrix.keyAt(index);
for (int j = 0; j < indexes.length; j++) {
if (key == indexes[j]) {
absents[j] = true;
break;
}
}
}
matrix.removeRange(fromIndex, toIndex);
assertEquals(matrix.size(), size - (toIndex - fromIndex));
verify(matrix, indexes, absents);
matrix.compact();
verify(matrix, indexes, absents);
}
}
@Test
public void testNestedArrays() {
final String name = "NestedArrays";