blob: 36249752444b5273be0e3b7eee7f4fc691ee6bd2 [file] [log] [blame]
package com.android.tools.r8.dex;
import com.android.tools.r8.graph.DexString;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import it.unimi.dsi.fastutil.objects.Object2LongMap;
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
import java.io.UTFDataFormatException;
import java.util.Comparator;
import java.util.Map;
public class ClassesChecksum {
private static final String PREFIX = "~~~";
private static final char PREFIX_CHAR0 = '~';
private static final char PREFIX_CHAR1 = '~';
private static final char PREFIX_CHAR2 = '~';
private final Object2LongMap<String> dictionary = new Object2LongOpenHashMap<>();
public ClassesChecksum() {
assert PREFIX.length() == 3;
assert PREFIX.charAt(0) == PREFIX_CHAR0;
assert PREFIX.charAt(1) == PREFIX_CHAR1;
assert PREFIX.charAt(2) == PREFIX_CHAR2;
}
private void append(JsonObject json) {
json.entrySet()
.forEach(
entry ->
dictionary.put(entry.getKey(), Long.parseLong(entry.getValue().getAsString(), 16)));
}
public void addChecksum(String classDescriptor, long crc) {
dictionary.put(classDescriptor, crc);
}
public Object2LongMap<String> getChecksums() {
return dictionary;
}
public String toJsonString() {
// In order to make printing of markers deterministic we sort the entries by key.
final JsonObject sortedJson = new JsonObject();
dictionary.object2LongEntrySet().stream()
.sorted(Comparator.comparing(Map.Entry::getKey))
.forEach(
entry ->
sortedJson.addProperty(entry.getKey(), Long.toString(entry.getLongValue(), 16)));
return "" + PREFIX_CHAR0 + PREFIX_CHAR1 + PREFIX_CHAR2 + sortedJson;
}
// Try to parse the string as a marker and append its content if successful.
public void tryParseAndAppend(DexString dexString) {
if (dexString.size > 2
&& dexString.content[0] == PREFIX_CHAR0
&& dexString.content[1] == PREFIX_CHAR1
&& dexString.content[2] == PREFIX_CHAR2) {
String str = dexString.toString().substring(3);
try {
JsonElement result = new JsonParser().parse(str);
if (result.isJsonObject()) {
append(result.getAsJsonObject());
}
} catch (JsonSyntaxException ignored) {}
}
}
/**
* Check if this string will definitely preceded the checksum marker.
*
* <p>If true is returned the string passed definitely preceded the checksum marker. If false is
* returned the string passed might still preceded, so this can give false negatives.
*
* @param string String to check if definitely preceded the checksum marker.
* @return If the string passed definitely preceded the checksum marker
*/
public static boolean definitelyPrecedesChecksumMarker(DexString string) {
try {
assert PREFIX.length() == 3;
char[] prefix = new char[PREFIX.length()];
int prefixLength = string.decodePrefix(prefix);
if (prefixLength == 0) {
return true;
}
if (prefix[0] != PREFIX_CHAR0) {
return prefix[0] < PREFIX_CHAR0;
}
if (prefixLength == 1) {
return true;
}
if (prefix[1] != PREFIX_CHAR1) {
return prefix[1] < PREFIX_CHAR1;
}
if (prefixLength == 2) {
return true;
}
return prefix[2] < PREFIX_CHAR2;
} catch (UTFDataFormatException e) {
throw new RuntimeException("Bad format", e);
}
}
}