1 // Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file 2 // for details. All rights reserved. Use of this source code is governed by a 3 // BSD-style license that can be found in the LICENSE file. 4 package com.android.tools.r8.utils; 5 6 import com.android.tools.r8.errors.Unreachable; 7 import java.security.MessageDigest; 8 import java.security.NoSuchAlgorithmException; 9 import java.util.Arrays; 10 import java.util.Collection; 11 import java.util.function.Function; 12 13 public class StringUtils { 14 15 private final static char[] IDENTIFIER_LETTERS 16 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_".toCharArray(); 17 private final static int NUMBER_OF_LETTERS = IDENTIFIER_LETTERS.length; 18 19 public enum BraceType { 20 PARENS, 21 SQUARE, 22 TUBORG, 23 NONE; 24 left()25 public String left() { 26 switch (this) { 27 case PARENS: return "("; 28 case SQUARE: return "["; 29 case TUBORG: return "{"; 30 case NONE: return ""; 31 default: throw new Unreachable("Invalid brace type: " + this); 32 } 33 } 34 right()35 public String right() { 36 switch (this) { 37 case PARENS: return ")"; 38 case SQUARE: return "]"; 39 case TUBORG: return "}"; 40 case NONE: return ""; 41 default: throw new Unreachable("Invalid brace type: " + this); 42 } 43 } 44 } 45 appendNonEmpty(StringBuilder builder, String pre, Object item, String post)46 public static void appendNonEmpty(StringBuilder builder, String pre, Object item, String post) { 47 if (item == null) { 48 return; 49 } 50 String text = item.toString(); 51 if (!text.isEmpty()) { 52 if (pre != null) { 53 builder.append(pre); 54 } 55 builder.append(text); 56 if (post != null) { 57 builder.append(post); 58 } 59 } 60 } 61 appendIndent(StringBuilder builder, String subject, int indent)62 public static StringBuilder appendIndent(StringBuilder builder, String subject, int indent) { 63 for (int i = 0; i < indent; i++) { 64 builder.append(" "); 65 } 66 builder.append(subject); 67 return builder; 68 } 69 appendLeftPadded(StringBuilder builder, String subject, int width)70 public static StringBuilder appendLeftPadded(StringBuilder builder, String subject, int width) { 71 for (int i = subject.length(); i < width; i++) { 72 builder.append(" "); 73 } 74 builder.append(subject); 75 return builder; 76 } 77 appendRightPadded(StringBuilder builder, String subject, int width)78 public static StringBuilder appendRightPadded(StringBuilder builder, String subject, int width) { 79 builder.append(subject); 80 for (int i = subject.length(); i < width; i++) { 81 builder.append(" "); 82 } 83 return builder; 84 } 85 append(StringBuilder builder, Collection<T> collection)86 public static <T> StringBuilder append(StringBuilder builder, Collection<T> collection) { 87 return append(builder, collection, ", ", BraceType.PARENS); 88 } 89 append(StringBuilder builder, Collection<T> collection, String seperator, BraceType brace)90 public static <T> StringBuilder append(StringBuilder builder, Collection<T> collection, 91 String seperator, BraceType brace) { 92 builder.append(brace.left()); 93 boolean first = true; 94 for (T element : collection) { 95 if (first) { 96 first = false; 97 } else { 98 builder.append(seperator); 99 } 100 builder.append(element); 101 } 102 builder.append(brace.right()); 103 return builder; 104 } 105 join(Collection<T> collection, String separator)106 public static <T> String join(Collection<T> collection, String separator) { 107 return join(collection, separator, BraceType.NONE); 108 } 109 join(String separator, String... strings)110 public static String join(String separator, String... strings) { 111 return join(Arrays.asList(strings), separator, BraceType.NONE); 112 } 113 join(Collection<T> collection, String separator, BraceType brace)114 public static <T> String join(Collection<T> collection, String separator, BraceType brace) { 115 return join(collection, separator, brace, Object::toString); 116 } 117 join(Collection<T> collection, String separator, BraceType brace, Function<T, String> fn)118 public static <T> String join(Collection<T> collection, String separator, BraceType brace, 119 Function<T, String> fn) { 120 StringBuilder builder = new StringBuilder(); 121 append(builder, ListUtils.map(collection, fn), separator, brace); 122 return builder.toString(); 123 } 124 zeroPrefix(int i, int width)125 public static String zeroPrefix(int i, int width) { 126 return zeroPrefixString(Integer.toString(i), width); 127 } 128 zeroPrefixString(String s, int width)129 private static String zeroPrefixString(String s, int width) { 130 String prefix = "0000000000000000"; 131 assert(width <= prefix.length()); 132 int prefixLength = width - s.length(); 133 if (prefixLength > 0) { 134 StringBuilder builder = new StringBuilder(); 135 builder.append(prefix, 0, prefixLength); 136 builder.append(s); 137 return builder.toString(); 138 } else { 139 return s; 140 } 141 } 142 hexString(int value, int width)143 public static String hexString(int value, int width) { 144 assert(0 <= width && width <= 8); 145 String hex = Integer.toHexString(value); 146 if (value >= 0) { 147 return zeroPrefixString(hex, width); 148 } else { 149 // Negative ints are always formatted as 8 characters. 150 assert(hex.length() == 8); 151 return hex; 152 } 153 } 154 hexString(long value, int width)155 public static String hexString(long value, int width) { 156 assert(0 <= width && width <= 16); 157 String hex = Long.toHexString(value); 158 if (value >= 0) { 159 return zeroPrefixString(hex, width); 160 } else { 161 // Negative longs are always formatted as 16 characters. 162 assert(hex.length() == 16); 163 return hex; 164 } 165 } 166 computeMD5Hash(String name)167 public static String computeMD5Hash(String name) { 168 byte[] digest = null; 169 try { 170 MessageDigest m = MessageDigest.getInstance("MD5"); 171 m.reset(); 172 m.update(name.getBytes()); 173 digest = m.digest(); 174 } catch (NoSuchAlgorithmException e) { 175 throw new RuntimeException(e); 176 } 177 return Arrays.toString(digest); 178 } 179 numberToIdentifier(char[] prefix, int nameCount, boolean addSemicolon)180 public static String numberToIdentifier(char[] prefix, int nameCount, boolean addSemicolon) { 181 // TODO(herhut): Add support for using numbers. 182 int size = addSemicolon ? 1 : 0; 183 int number = nameCount; 184 while (number >= NUMBER_OF_LETTERS) { 185 number /= NUMBER_OF_LETTERS; 186 size++; 187 } 188 size++; 189 char characters[] = Arrays.copyOfRange(prefix, 0, prefix.length + size); 190 number = nameCount; 191 192 int i = prefix.length; 193 while (number >= NUMBER_OF_LETTERS) { 194 characters[i++] = IDENTIFIER_LETTERS[number % NUMBER_OF_LETTERS]; 195 number /= NUMBER_OF_LETTERS; 196 } 197 characters[i++] = IDENTIFIER_LETTERS[number - 1]; 198 if (addSemicolon) { 199 characters[i++] = ';'; 200 } 201 assert i == characters.length; 202 203 return new String(characters); 204 } 205 } 206