[ApiModel] Use a mapped byte buffer if possible
Bug: b/213552119
Change-Id: Ic56ab34ddb863ade83fa1f81ce031466186864b7
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiDataAccess.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiDataAccess.java
index ada88b6..320bb09 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiDataAccess.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiDataAccess.java
@@ -5,15 +5,25 @@
package com.android.tools.r8.androidapi;
import static com.android.tools.r8.lightir.ByteUtils.unsetBitAtIndex;
+import static com.android.tools.r8.utils.ZipUtils.getOffsetOfResourceInZip;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
+import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.io.ByteStreams;
import com.google.common.primitives.Ints;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
import java.util.function.BiPredicate;
/**
@@ -36,7 +46,7 @@
private static class PositionAndLength {
- private static PositionAndLength EMPTY = new PositionAndLength(0, 0);
+ private static final PositionAndLength EMPTY = new PositionAndLength(0, 0);
private final int position;
private final int length;
@@ -50,7 +60,7 @@
if (position == 0 && length == 0) {
return EMPTY;
}
- if ((position < 0 && length != 0) || (position > 0 && length == 0)) {
+ if ((position < 0 && length > 0) || (position > 0 && length == 0)) {
assert false : "Unexpected position and length";
return EMPTY;
}
@@ -74,6 +84,58 @@
}
}
+ public static AndroidApiDataAccess create(DiagnosticsHandler diagnosticsHandler) {
+ URL resource = AndroidApiDataAccess.class.getClassLoader().getResource(RESOURCE_NAME);
+ if (resource == null) {
+ throw new CompilationError("Could not find the api database at " + RESOURCE_NAME);
+ }
+ try {
+ // The resource is encoded as protocol and a path, where we should have one of either:
+ // protocol: file, path: <path-to-file>
+ // protocol: jar, path: file:<path-to-jar>!/<resource-name-in-jar>
+ if (resource.getProtocol().equals("file")) {
+ return getDataAccessFromPathAndOffset(Paths.get(resource.getPath()), 0);
+ } else if (resource.getProtocol().equals("jar") && resource.getPath().startsWith("file:")) {
+ // The path is on form 'file:<path-to-jar>!/<resource-name-in-jar>
+ String path = resource.getPath().substring(5);
+ int jarEntrySeparator = path.indexOf('!');
+ if (jarEntrySeparator > 0) {
+ String file = path.substring(0, jarEntrySeparator);
+ String databaseEntry = path.substring(jarEntrySeparator + 2);
+ Path jarPath = Paths.get(file);
+ long offsetInJar = getOffsetOfResourceInZip(jarPath, databaseEntry);
+ if (offsetInJar > 0) {
+ return getDataAccessFromPathAndOffset(jarPath, offsetInJar);
+ }
+ }
+ }
+ } catch (Exception e) {
+ diagnosticsHandler.warning(new ExceptionDiagnostic(e));
+ return null;
+ }
+ diagnosticsHandler.warning(
+ new StringDiagnostic(
+ "Unable to use a memory mapped byte buffer to access the api database. Falling back"
+ + " to loading the database into program which requires more memory"));
+ try (InputStream apiInputStream =
+ AndroidApiDataAccess.class.getClassLoader().getResourceAsStream(RESOURCE_NAME)) {
+ if (apiInputStream == null) {
+ throw new CompilationError("Could not find the api database at: " + resource);
+ }
+ return new AndroidApiDataAccessInMemory(ByteStreams.toByteArray(apiInputStream));
+ } catch (IOException e) {
+ throw new CompilationError("Could not read the api database.", e);
+ }
+ }
+
+ private static AndroidApiDataAccessByteMapped getDataAccessFromPathAndOffset(
+ Path path, long offset) throws IOException {
+ FileChannel fileChannel = (FileChannel) Files.newByteChannel(path, StandardOpenOption.READ);
+ MappedByteBuffer mappedByteBuffer =
+ fileChannel.map(FileChannel.MapMode.READ_ONLY, offset, fileChannel.size() - offset);
+ return new AndroidApiDataAccessByteMapped(mappedByteBuffer);
+ }
+
public static int entrySizeInBitsForConstantPoolMap() {
return ENTRY_SIZE_IN_BITS_FOR_CONSTANT_POOL_MAP;
}
@@ -106,25 +168,44 @@
return PAYLOAD_OFFSET_WITH_LENGTH;
}
- static int constantPoolOffset() {
+ /** The start of the constant pool */
+ public static int constantPoolOffset() {
return 4;
}
- static int constantPoolHashMapOffset(int constantPoolSize) {
+ /** The start of the constant pool hash map. */
+ public static int constantPoolHashMapOffset(int constantPoolSize) {
return (constantPoolSize * constantPoolEntrySize()) + constantPoolOffset();
}
- static int apiLevelHashMapOffset(int constantPoolSize) {
+ /** The start of the api level hash map. */
+ public static int apiLevelHashMapOffset(int constantPoolSize) {
int constantPoolHashMapSize =
(1 << entrySizeInBitsForConstantPoolMap()) * constantPoolMapEntrySize();
return constantPoolHashMapOffset(constantPoolSize) + constantPoolHashMapSize;
}
- static int payloadOffset(int constantPoolSize) {
+ /** The start of the payload section. */
+ public static int payloadOffset(int constantPoolSize) {
int apiLevelSize = (1 << entrySizeInBitsForApiLevelMap()) * apiLevelHashMapEntrySize();
return apiLevelHashMapOffset(constantPoolSize) + apiLevelSize;
}
+ /** The actual byte index of the constant pool index. */
+ public int constantPoolIndexOffset(int index) {
+ return constantPoolOffset() + (index * constantPoolEntrySize());
+ }
+
+ /** The actual byte index of the constant pool hash key. */
+ protected int constantPoolHashMapIndexOffset(int hash) {
+ return constantPoolHashMapOffset(getConstantPoolSize()) + (hash * constantPoolMapEntrySize());
+ }
+
+ /** The actual byte index of the api hash key. */
+ protected int apiLevelHashMapIndexOffset(int hash) {
+ return apiLevelHashMapOffset(getConstantPoolSize()) + (hash * apiLevelHashMapEntrySize());
+ }
+
static int readIntFromOffset(byte[] data, int offset) {
return Ints.fromBytes(data[offset], data[offset + 1], data[offset + 2], data[offset + 3]);
}
@@ -137,11 +218,7 @@
abstract int readConstantPoolSize();
- abstract PositionAndLength getConstantPoolPayloadOffset(int index);
-
- abstract PositionAndLength getConstantPoolHashMapPayloadOffset(int hash);
-
- abstract PositionAndLength getApiLevelHashMapPayloadOffset(int hash);
+ abstract PositionAndLength readPositionAndLength(int offset);
abstract boolean payloadHasConstantPoolValue(int offset, int length, byte[] value);
@@ -173,7 +250,7 @@
public int getConstantPoolIndex(DexString string) {
PositionAndLength constantPoolIndex =
- getConstantPoolHashMapPayloadOffset(constantPoolHash(string));
+ readPositionAndLength(constantPoolHashMapIndexOffset(constantPoolHash(string)));
if (constantPoolIndex.isEmpty()) {
return -1;
}
@@ -187,56 +264,108 @@
} else {
assert length > 0;
return payloadContainsConstantPoolValue(
- position, length, string.content, this::isConstantPoolEntry);
+ payloadOffset(getConstantPoolSize()) + position,
+ length,
+ string.content,
+ this::isConstantPoolEntry);
}
return -1;
}
public boolean isConstantPoolEntry(int index, byte[] value) {
- PositionAndLength constantPoolPayloadOffset = getConstantPoolPayloadOffset(index);
+ PositionAndLength constantPoolPayloadOffset =
+ readPositionAndLength(constantPoolIndexOffset(index));
if (constantPoolPayloadOffset.isEmpty()) {
return false;
}
+ if (value.length != constantPoolPayloadOffset.getLength()) {
+ return false;
+ }
return payloadHasConstantPoolValue(
- constantPoolPayloadOffset.getPosition(), constantPoolPayloadOffset.getLength(), value);
+ payloadOffset(getConstantPoolSize()) + constantPoolPayloadOffset.getPosition(),
+ constantPoolPayloadOffset.getLength(),
+ value);
}
public byte getApiLevelForReference(byte[] serialized, DexReference reference) {
PositionAndLength apiLevelPayloadOffset =
- getApiLevelHashMapPayloadOffset(apiLevelHash(reference));
+ readPositionAndLength(apiLevelHashMapIndexOffset(apiLevelHash(reference)));
if (apiLevelPayloadOffset.isEmpty()) {
return 0;
}
return readApiLevelForPayloadOffset(
- apiLevelPayloadOffset.getPosition(), apiLevelPayloadOffset.getLength(), serialized);
+ payloadOffset(getConstantPoolSize()) + apiLevelPayloadOffset.getPosition(),
+ apiLevelPayloadOffset.getLength(),
+ serialized);
}
- public static byte findApiForReferenceHelper(byte[] data, int offset, int length, byte[] value) {
- int index = offset;
- while (index < offset + length) {
- // Read size of entry
- int lengthOfEntry = Ints.fromBytes(ZERO_BYTE, ZERO_BYTE, data[index], data[index + 1]);
- int startIndex = index + 2;
- int endIndex = startIndex + lengthOfEntry;
- if (isSerializedDescriptor(value, data, startIndex, lengthOfEntry)) {
- return data[endIndex];
- }
- index = endIndex + 1;
- }
- return 0;
- }
+ public static class AndroidApiDataAccessByteMapped extends AndroidApiDataAccess {
- protected static boolean isSerializedDescriptor(
- byte[] serialized, byte[] candidate, int offset, int length) {
- if (serialized.length != length) {
- return false;
+ private final MappedByteBuffer mappedByteBuffer;
+
+ public AndroidApiDataAccessByteMapped(MappedByteBuffer mappedByteBuffer) {
+ this.mappedByteBuffer = mappedByteBuffer;
}
- for (int i = 0; i < length; i++) {
- if (serialized[i] != candidate[i + offset]) {
- return false;
+
+ @Override
+ int readConstantPoolSize() {
+ return mappedByteBuffer.getInt(0);
+ }
+
+ @Override
+ public PositionAndLength readPositionAndLength(int offset) {
+ return PositionAndLength.create(
+ mappedByteBuffer.getInt(offset), mappedByteBuffer.getShort(offset + 4));
+ }
+
+ @Override
+ boolean payloadHasConstantPoolValue(int offset, int length, byte[] value) {
+ assert length == value.length;
+ mappedByteBuffer.position(offset);
+ for (byte expected : value) {
+ if (expected != mappedByteBuffer.get()) {
+ return false;
+ }
}
+ return true;
}
- return true;
+
+ @Override
+ int payloadContainsConstantPoolValue(
+ int offset, int length, byte[] value, BiPredicate<Integer, byte[]> predicate) {
+ for (int i = offset; i < offset + length; i += 2) {
+ // Do not use mappedByteBuffer.getShort() since that will add the sign.
+ int index =
+ Ints.fromBytes(
+ ZERO_BYTE, ZERO_BYTE, mappedByteBuffer.get(i), mappedByteBuffer.get(i + 1));
+ if (predicate.test(index, value)) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ byte readApiLevelForPayloadOffset(int offset, int length, byte[] value) {
+ int currentOffset = offset;
+ while (currentOffset < offset + length) {
+ // Read the length
+ int lengthOfEntry =
+ Ints.fromBytes(
+ ZERO_BYTE,
+ ZERO_BYTE,
+ mappedByteBuffer.get(currentOffset),
+ mappedByteBuffer.get(currentOffset + 1));
+ int startPosition = currentOffset + 2;
+ if (value.length == lengthOfEntry
+ && payloadHasConstantPoolValue(startPosition, lengthOfEntry, value)) {
+ return mappedByteBuffer.get(startPosition + lengthOfEntry);
+ }
+ // Advance our current position + length of entry + api level.
+ currentOffset = startPosition + lengthOfEntry + 1;
+ }
+ return -1;
+ }
}
public static class AndroidApiDataAccessInMemory extends AndroidApiDataAccess {
@@ -247,62 +376,36 @@
this.data = data;
}
- public static AndroidApiDataAccessInMemory create() {
- byte[] data;
- try (InputStream apiInputStream =
- AndroidApiDataAccess.class.getClassLoader().getResourceAsStream(RESOURCE_NAME); ) {
- if (apiInputStream == null) {
- URL resource = AndroidApiDataAccess.class.getClassLoader().getResource(RESOURCE_NAME);
- throw new CompilationError("Could not find the api database at: " + resource);
- }
- data = ByteStreams.toByteArray(apiInputStream);
- } catch (IOException e) {
- throw new CompilationError("Could not read the api database.", e);
- }
- return new AndroidApiDataAccessInMemory(data);
- }
-
@Override
public int readConstantPoolSize() {
return readIntFromOffset(data, 0);
}
@Override
- PositionAndLength getConstantPoolPayloadOffset(int index) {
- int offset = constantPoolOffset() + (index * constantPoolEntrySize());
- return PositionAndLength.create(data, offset);
- }
-
- @Override
- PositionAndLength getConstantPoolHashMapPayloadOffset(int hash) {
- int offset =
- constantPoolHashMapOffset(getConstantPoolSize()) + (hash * constantPoolMapEntrySize());
- return PositionAndLength.create(data, offset);
- }
-
- @Override
- PositionAndLength getApiLevelHashMapPayloadOffset(int hash) {
- int offset =
- apiLevelHashMapOffset(getConstantPoolSize()) + (hash * apiLevelHashMapEntrySize());
+ PositionAndLength readPositionAndLength(int offset) {
return PositionAndLength.create(data, offset);
}
@Override
boolean payloadHasConstantPoolValue(int offset, int length, byte[] value) {
- return isSerializedDescriptor(
- value, data, payloadOffset(getConstantPoolSize()) + offset, length);
+ if (value.length != length) {
+ return false;
+ }
+ for (int i = 0; i < length; i++) {
+ if (value[i] != data[i + offset]) {
+ return false;
+ }
+ }
+ return true;
}
@Override
int payloadContainsConstantPoolValue(
int offset, int length, byte[] value, BiPredicate<Integer, byte[]> predicate) {
- int payloadOffset = payloadOffset(getConstantPoolSize());
- int startInPayload = payloadOffset + offset;
- int endInPayload = startInPayload + length;
- if (data.length < endInPayload) {
+ if (data.length < length) {
return -1;
}
- for (int i = startInPayload; i < endInPayload; i += 2) {
+ for (int i = offset; i < offset + length; i += 2) {
int index = Ints.fromBytes(ZERO_BYTE, ZERO_BYTE, data[i], data[i + 1]);
if (predicate.test(index, value)) {
return index;
@@ -313,8 +416,18 @@
@Override
byte readApiLevelForPayloadOffset(int offset, int length, byte[] value) {
- return findApiForReferenceHelper(
- data, payloadOffset(getConstantPoolSize()) + offset, length, value);
+ int index = offset;
+ while (index < offset + length) {
+ // Read size of entry
+ int lengthOfEntry = Ints.fromBytes(ZERO_BYTE, ZERO_BYTE, data[index], data[index + 1]);
+ int startIndex = index + 2;
+ int endIndex = startIndex + lengthOfEntry;
+ if (payloadHasConstantPoolValue(startIndex, lengthOfEntry, value)) {
+ return data[endIndex];
+ }
+ index = endIndex + 1;
+ }
+ return 0;
}
}
}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
index 5e0a407..cbe1a03 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
@@ -7,7 +7,7 @@
import static com.android.tools.r8.lightir.ByteUtils.isU2;
import static com.android.tools.r8.utils.AndroidApiLevel.ANDROID_PLATFORM;
-import com.android.tools.r8.androidapi.AndroidApiDataAccess.AndroidApiDataAccessInMemory;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexReference;
@@ -126,13 +126,14 @@
private final Map<DexReference, AndroidApiLevel> lookupCache = new ConcurrentHashMap<>();
private final Map<DexString, Integer> constantPoolCache = new ConcurrentHashMap<>();
+ private final DiagnosticsHandler diagnosticsHandler;
private static volatile AndroidApiDataAccess dataAccess;
- private static AndroidApiDataAccess getDataAccess() {
+ private static AndroidApiDataAccess getDataAccess(DiagnosticsHandler diagnosticsHandler) {
if (dataAccess == null) {
synchronized (AndroidApiDataAccess.class) {
if (dataAccess == null) {
- dataAccess = AndroidApiDataAccessInMemory.create();
+ dataAccess = AndroidApiDataAccess.create(diagnosticsHandler);
}
}
}
@@ -140,7 +141,9 @@
}
public AndroidApiLevelHashingDatabaseImpl(
- List<AndroidApiForHashingReference> predefinedApiTypeLookup) {
+ List<AndroidApiForHashingReference> predefinedApiTypeLookup,
+ DiagnosticsHandler diagnosticsHandler) {
+ this.diagnosticsHandler = diagnosticsHandler;
predefinedApiTypeLookup.forEach(
predefinedApiReference -> {
// Do not use computeIfAbsent since a return value of null implies the key should not be
@@ -169,7 +172,7 @@
private int getConstantPoolId(DexString string) {
return constantPoolCache.computeIfAbsent(
- string, key -> getDataAccess().getConstantPoolIndex(string));
+ string, key -> getDataAccess(diagnosticsHandler).getConstantPoolIndex(string));
}
private AndroidApiLevel lookupApiLevel(DexReference reference) {
@@ -190,7 +193,8 @@
return ANDROID_PLATFORM;
} else {
byte apiLevelForReference =
- getDataAccess().getApiLevelForReference(uniqueDescriptorForReference, ref);
+ getDataAccess(diagnosticsHandler)
+ .getApiLevelForReference(uniqueDescriptorForReference, ref);
return (apiLevelForReference <= 0)
? ANDROID_PLATFORM
: AndroidApiLevel.getAndroidApiLevel(apiLevelForReference);
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
index 0e08965..1434d03 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -30,7 +30,8 @@
this.apiLevelCompute = apiLevelCompute;
factory = appView.dexItemFactory();
androidApiLevelDatabase =
- new AndroidApiLevelHashingDatabaseImpl(predefinedApiTypeLookupForHashing);
+ new AndroidApiLevelHashingDatabaseImpl(
+ predefinedApiTypeLookupForHashing, appView.reporter());
}
public static AndroidApiReferenceLevelCache create(
diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
index b3808a7..bf60f68 100644
--- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -45,6 +45,9 @@
public class ZipUtils {
+ // Beginning of extra field length: https://en.wikipedia.org/wiki/ZIP_(file_format)
+ private static final int EXTRA_FIELD_LENGTH_OFFSET = 30;
+
public static void writeResourcesToZip(
List<ProgramResource> resources,
Set<DataEntryResource> dataResources,
@@ -295,4 +298,25 @@
public static String zipEntryNameForClass(Class<?> clazz) {
return DescriptorUtils.getClassBinaryName(clazz) + CLASS_EXTENSION;
}
+
+ public static long getOffsetOfResourceInZip(Path path, String entry) throws IOException {
+ // Look into the jar file to see find the offset.
+ ZipFile zipFile = new ZipFile(path.toFile());
+ Enumeration<? extends ZipEntry> entries = zipFile.entries();
+ long offset = 0;
+ while (entries.hasMoreElements()) {
+ ZipEntry zipEntry = entries.nextElement();
+ byte[] extra = zipEntry.getExtra();
+ offset +=
+ EXTRA_FIELD_LENGTH_OFFSET
+ + zipEntry.getName().length()
+ + (extra == null ? 0 : extra.length);
+ if (zipEntry.getName().equals(entry)) {
+ return zipEntry.getSize() == zipEntry.getCompressedSize() ? offset : -1;
+ } else if (!zipEntry.isDirectory()) {
+ offset += zipEntry.getCompressedSize();
+ }
+ }
+ return -1;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
index a409fd6..708858e 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
@@ -10,6 +10,7 @@
import static com.android.tools.r8.lightir.ByteUtils.isU2;
import static com.android.tools.r8.lightir.ByteUtils.setBitAtIndex;
import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
@@ -211,12 +212,12 @@
Map<Integer, List<Pair<DexReference, AndroidApiLevel>>> generationMap = new HashMap<>();
ConstantPool constantPool = new ConstantPool();
- int constantPoolMapSize = 1 << AndroidApiDataAccess.entrySizeInBitsForConstantPoolMap();
- int apiMapSize = 1 << AndroidApiDataAccess.entrySizeInBitsForApiLevelMap();
+ int constantPoolHashMapSize = 1 << AndroidApiDataAccess.entrySizeInBitsForConstantPoolMap();
+ int apiHashMapSize = 1 << AndroidApiDataAccess.entrySizeInBitsForApiLevelMap();
for (Entry<DexReference, AndroidApiLevel> entry : referenceMap.entrySet()) {
int newCode = AndroidApiDataAccess.apiLevelHash(entry.getKey());
- assert newCode >= 0 && newCode <= apiMapSize;
+ assert newCode >= 0 && newCode <= apiHashMapSize;
generationMap
.computeIfAbsent(newCode, ignoreKey(ArrayList::new))
.add(Pair.create(entry.getKey(), entry.getValue()));
@@ -238,6 +239,7 @@
outputStream.writeInt(constantPool.size());
// Write constant pool consisting of <u4:payload_offset><u2:length>.
+ assertEquals(AndroidApiDataAccess.constantPoolOffset(), outputStream.size());
IntBox lastReadIndex = new IntBox(-1);
constantPool.forEach(
(string, id) -> {
@@ -252,14 +254,14 @@
constantPool.forEach(
(string, id) -> {
int constantPoolHash = constantPoolHash(string);
- assert constantPoolHash >= 0 && constantPoolHash <= constantPoolMapSize;
+ assert constantPoolHash >= 0 && constantPoolHash <= constantPoolHashMapSize;
constantPoolLookupTable
.computeIfAbsent(constantPoolHash, ignoreKey(ArrayList::new))
.add(id);
});
- int[] constantPoolEntries = new int[constantPoolMapSize];
- int[] constantPoolEntryLengths = new int[constantPoolMapSize];
+ int[] constantPoolEntries = new int[constantPoolHashMapSize];
+ int[] constantPoolEntryLengths = new int[constantPoolHashMapSize];
for (Entry<Integer, List<Integer>> entry : constantPoolLookupTable.entrySet()) {
// Tag if we have a unique value
if (entry.getValue().size() == 1) {
@@ -276,13 +278,15 @@
}
}
// Write constant pool lookup entries consisting of <u4:payload_offset><u2:length>
+ assertEquals(
+ AndroidApiDataAccess.constantPoolHashMapOffset(constantPool.size()), outputStream.size());
for (int i = 0; i < constantPoolEntries.length; i++) {
outputStream.writeInt(constantPoolEntries[i]);
outputStream.writeShort(constantPoolEntryLengths[i]);
}
- int[] apiOffsets = new int[apiMapSize];
- int[] apiOffsetLengths = new int[apiMapSize];
+ int[] apiOffsets = new int[apiHashMapSize];
+ int[] apiOffsetLengths = new int[apiHashMapSize];
for (Entry<Integer, Pair<Integer, Integer>> hashIndexAndOffset : offsetMap.entrySet()) {
assert apiOffsets[hashIndexAndOffset.getKey()] == 0;
Pair<Integer, Integer> value = hashIndexAndOffset.getValue();
@@ -293,6 +297,8 @@
}
// Write api lookup entries consisting of <u4:payload_offset><u2:length>
+ assertEquals(
+ AndroidApiDataAccess.apiLevelHashMapOffset(constantPool.size()), outputStream.size());
for (int i = 0; i < apiOffsets.length; i++) {
outputStream.writeInt(apiOffsets[i]);
outputStream.writeShort(apiOffsetLengths[i]);
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
index dcf406d..d6fba52 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
@@ -8,6 +8,7 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
@@ -117,8 +118,9 @@
AndroidApiVersionsXmlParser.getParsedApiClasses(
ToolHelper.getApiVersionsXmlFile(API_LEVEL).toFile(), API_LEVEL);
DexItemFactory factory = new DexItemFactory();
+ TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
AndroidApiLevelHashingDatabaseImpl androidApiLevelDatabase =
- new AndroidApiLevelHashingDatabaseImpl(ImmutableList.of());
+ new AndroidApiLevelHashingDatabaseImpl(ImmutableList.of(), diagnosticsHandler);
parsedApiClasses.forEach(
parsedApiClass -> {
DexType type = factory.createType(parsedApiClass.getClassReference().getDescriptor());
@@ -147,6 +149,7 @@
.isLessThanOrEqualTo(fieldApiLevel);
}));
});
+ diagnosticsHandler.assertNoMessages();
}
/**