|  | 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); | 
|  | } | 
|  | } | 
|  | } |