Merge "Refactoring and tweaking TimeSparseArray"

This commit is contained in:
TreeHugger Robot
2022-10-06 06:30:32 +00:00
committed by Android (Google) Code Review
5 changed files with 121 additions and 35 deletions

View File

@@ -14,13 +14,11 @@
* under the License.
*/
package android.app.usage;
import android.util.LongSparseArray;
import android.util.Slog;
package android.util;
/**
* An array that indexes by a long timestamp, representing milliseconds since the epoch.
* @param <E> The type of values this container maps to a timestamp.
*
* {@hide}
*/
@@ -29,53 +27,41 @@ public class TimeSparseArray<E> extends LongSparseArray<E> {
private boolean mWtfReported;
public TimeSparseArray() {
super();
}
/**
* Finds the index of the first element whose timestamp is greater or equal to
* the given time.
*
* @param time The timestamp for which to search the array.
* @return The index of the matched element, or -1 if no such match exists.
* @return The smallest {@code index} for which {@code (keyAt(index) >= timeStamp)} is
* {@code true}, or {@link #size() size} if no such {@code index} exists.
*/
public int closestIndexOnOrAfter(long time) {
// This is essentially a binary search, except that if no match is found
// the closest index is returned.
final int size = size();
int result = size;
int lo = 0;
int hi = size - 1;
int mid = -1;
long key = -1;
while (lo <= hi) {
mid = lo + ((hi - lo) / 2);
key = keyAt(mid);
final int mid = lo + ((hi - lo) / 2);
final long key = keyAt(mid);
if (time > key) {
lo = mid + 1;
} else if (time < key) {
hi = mid - 1;
result = mid;
} else {
return mid;
}
}
if (time < key) {
return mid;
} else if (time > key && lo < size) {
return lo;
} else {
return -1;
}
return result;
}
/**
* {@inheritDoc}
*
* <p> As this container is being used only to keep {@link android.util.AtomicFile files},
* there should not be any collisions. Reporting a {@link Slog#wtf(String, String)} in case that
* happens, as that will lead to one whole file being dropped.
* <p> This container can store only one value for each timestamp. And so ideally, the caller
* should ensure that there are no collisions. Reporting a {@link Slog#wtf(String, String)}
* if that happens, as that will lead to the previous value being overwritten.
*/
@Override
public void put(long key, E value) {
@@ -93,16 +79,13 @@ public class TimeSparseArray<E> extends LongSparseArray<E> {
* the given time.
*
* @param time The timestamp for which to search the array.
* @return The index of the matched element, or -1 if no such match exists.
* @return The largest {@code index} for which {@code (keyAt(index) <= timeStamp)} is
* {@code true}, or -1 if no such {@code index} exists.
*/
public int closestIndexOnOrBefore(long time) {
final int index = closestIndexOnOrAfter(time);
if (index < 0) {
// Everything is larger, so we use the last element, or -1 if the list is empty.
return size() - 1;
}
if (keyAt(index) == time) {
if (index < size() && keyAt(index) == time) {
return index;
}
return index - 1;

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.util;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Tests for {@link TimeSparseArray}.
* This class only tests subclass specific functionality. Tests for the super class
* {@link LongSparseArray} should be covered under {@link LongSparseArrayTest}.
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TimeSparseArrayTest {
@Test
public void closestIndexOnOrAfter() {
final TimeSparseArray<Object> timeSparseArray = new TimeSparseArray<>();
// Values don't matter for this test.
timeSparseArray.put(51, new Object());
timeSparseArray.put(10, new Object());
timeSparseArray.put(59, new Object());
assertThat(timeSparseArray.size()).isEqualTo(3);
// Testing any number arbitrarily smaller than 10.
assertThat(timeSparseArray.closestIndexOnOrAfter(-141213)).isEqualTo(0);
for (long time = -43; time <= 10; time++) {
assertThat(timeSparseArray.closestIndexOnOrAfter(time)).isEqualTo(0);
}
for (long time = 11; time <= 51; time++) {
assertThat(timeSparseArray.closestIndexOnOrAfter(time)).isEqualTo(1);
}
for (long time = 52; time <= 59; time++) {
assertThat(timeSparseArray.closestIndexOnOrAfter(time)).isEqualTo(2);
}
for (long time = 60; time <= 102; time++) {
assertThat(timeSparseArray.closestIndexOnOrAfter(time)).isEqualTo(3);
}
// Testing any number arbitrarily larger than 59.
assertThat(timeSparseArray.closestIndexOnOrAfter(15332)).isEqualTo(3);
}
@Test
public void closestIndexOnOrBefore() {
final TimeSparseArray<Object> timeSparseArray = new TimeSparseArray<>();
// Values don't matter for this test.
timeSparseArray.put(21, new Object());
timeSparseArray.put(4, new Object());
timeSparseArray.put(91, new Object());
timeSparseArray.put(39, new Object());
assertThat(timeSparseArray.size()).isEqualTo(4);
// Testing any number arbitrarily smaller than 4.
assertThat(timeSparseArray.closestIndexOnOrBefore(-1478133)).isEqualTo(-1);
for (long time = -42; time < 4; time++) {
assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(-1);
}
for (long time = 4; time < 21; time++) {
assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(0);
}
for (long time = 21; time < 39; time++) {
assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(1);
}
for (long time = 39; time < 91; time++) {
assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(2);
}
for (long time = 91; time < 109; time++) {
assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(3);
}
// Testing any number arbitrarily larger than 91.
assertThat(timeSparseArray.closestIndexOnOrBefore(1980732)).isEqualTo(3);
}
}

View File

@@ -24,7 +24,6 @@ import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
@@ -32,6 +31,7 @@ import android.content.Context;
import android.content.res.Configuration;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.AtomicFile;
import android.util.TimeSparseArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;

View File

@@ -18,7 +18,6 @@ package com.android.server.usage;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
@@ -29,6 +28,7 @@ import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeSparseArray;
import android.util.TimeUtils;
import com.android.internal.annotations.VisibleForTesting;

View File

@@ -34,7 +34,6 @@ import android.annotation.Nullable;
import android.app.usage.ConfigurationStats;
import android.app.usage.EventList;
import android.app.usage.EventStats;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
@@ -50,6 +49,7 @@ import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArrayMap;
import android.util.SparseIntArray;
import android.util.TimeSparseArray;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;