1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package libcore.util; 18 19 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; 20 21 import android.annotation.SystemApi; 22 23 /** 24 * Hexadecimal encoding where each byte is represented by two hexadecimal digits. 25 * @hide 26 */ 27 @SystemApi(client = MODULE_LIBRARIES) 28 public class HexEncoding { 29 30 private static final char[] LOWER_CASE_DIGITS = { 31 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 32 }; 33 34 private static final char[] UPPER_CASE_DIGITS = { 35 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' 36 }; 37 38 /** Hidden constructor to prevent instantiation. */ HexEncoding()39 private HexEncoding() {} 40 41 /** 42 * Encodes the provided byte as a two-digit hexadecimal String value. 43 * 44 * @param b byte to encode 45 * @param upperCase {@code true} to use uppercase letters, {@code false} 46 * for lowercase 47 * @return the encoded string 48 * 49 * @hide 50 */ 51 @SystemApi(client = MODULE_LIBRARIES) encodeToString(byte b, boolean upperCase)52 public static String encodeToString(byte b, boolean upperCase) { 53 char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS; 54 char[] buf = new char[2]; // We always want two digits. 55 buf[0] = digits[(b >> 4) & 0xf]; 56 buf[1] = digits[b & 0xf]; 57 return new String(buf, 0, 2); 58 } 59 60 /** 61 * Encodes the provided data as a sequence of hexadecimal characters. 62 * 63 * @param data byte array to encode 64 * @return the encoded data, using uppercase letters 65 * 66 * @hide 67 */ 68 @SystemApi(client = MODULE_LIBRARIES) encode(byte[] data)69 public static char[] encode(byte[] data) { 70 return encode(data, 0, data.length, true /* upperCase */); 71 } 72 73 /** 74 * Encodes the provided data as a sequence of hexadecimal characters. 75 * 76 * @param data byte array to encode 77 * @param upperCase {@code true} to use uppercase letters, {@code false} 78 * for lowercase 79 * @return the encoded data 80 * 81 * @hide 82 */ 83 @SystemApi(client = MODULE_LIBRARIES) encode(byte[] data, boolean upperCase)84 public static char[] encode(byte[] data, boolean upperCase) { 85 return encode(data, 0, data.length, upperCase); 86 } 87 88 /** 89 * Encodes the provided data as a sequence of hexadecimal characters. 90 * 91 * @param data byte array containing the data to encode 92 * @param offset offset of the data to encode in the {@code data} array 93 * @param len length of the data to encode in the {@code data} array 94 * @return the encoded data, using uppercase letters 95 * 96 * @hide 97 */ 98 @SystemApi(client = MODULE_LIBRARIES) encode(byte[] data, int offset, int len)99 public static char[] encode(byte[] data, int offset, int len) { 100 return encode(data, offset, len, true /* upperCase */); 101 } 102 103 /** 104 * Encodes the provided data as a sequence of hexadecimal characters. 105 */ encode(byte[] data, int offset, int len, boolean upperCase)106 private static char[] encode(byte[] data, int offset, int len, boolean upperCase) { 107 char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS; 108 char[] result = new char[len * 2]; 109 for (int i = 0; i < len; i++) { 110 byte b = data[offset + i]; 111 int resultIndex = 2 * i; 112 result[resultIndex] = (digits[(b >> 4) & 0x0f]); 113 result[resultIndex + 1] = (digits[b & 0x0f]); 114 } 115 116 return result; 117 } 118 119 /** 120 * Encodes the provided data as a sequence of hexadecimal characters. 121 * 122 * @param data byte array to encode 123 * @return the encoded data, using uppercase letters 124 * 125 * @hide 126 */ 127 @SystemApi(client = MODULE_LIBRARIES) encodeToString(byte[] data)128 public static String encodeToString(byte[] data) { 129 return encodeToString(data, true /* upperCase */); 130 } 131 132 /** 133 * Encodes the provided data as a sequence of hexadecimal characters. 134 * 135 * @param data byte array to encode. 136 * @param upperCase {@code true} to use uppercase letters, {@code false} 137 * for lowercase 138 * @return the encoded data 139 * 140 * @hide 141 */ 142 @SystemApi(client = MODULE_LIBRARIES) encodeToString(byte[] data, boolean upperCase)143 public static String encodeToString(byte[] data, boolean upperCase) { 144 return new String(encode(data, upperCase)); 145 } 146 147 /** 148 * Decodes the provided hexadecimal sequence. Odd-length inputs are not 149 * allowed. 150 * 151 * @param encoded string of hexadecimal characters to decode. Letters 152 * can be either uppercase or lowercase. 153 * @return the decoded data 154 * @throws IllegalArgumentException if the input is malformed 155 * 156 * @hide 157 */ 158 @SystemApi(client = MODULE_LIBRARIES) decode(String encoded)159 public static byte[] decode(String encoded) throws IllegalArgumentException { 160 return decode(encoded.toCharArray()); 161 } 162 163 /** 164 * Decodes the provided hexadecimal sequence. 165 * 166 * @param encoded string of hexadecimal characters to decode. Letters 167 * can be either uppercase or lowercase. 168 * @param allowSingleChar If {@code true} odd-length inputs are allowed and 169 * the first character is interpreted as the lower bits of the first 170 * result byte. If {@code false} odd-length inputs are not allowed. 171 * @return the decoded data 172 * @throws IllegalArgumentException if the input is malformed 173 * 174 * @hide 175 */ 176 @SystemApi(client = MODULE_LIBRARIES) decode(String encoded, boolean allowSingleChar)177 public static byte[] decode(String encoded, boolean allowSingleChar) 178 throws IllegalArgumentException { 179 return decode(encoded.toCharArray(), allowSingleChar); 180 } 181 182 /** 183 * Decodes the provided hexadecimal sequence. Odd-length inputs are not 184 * allowed. 185 * 186 * @param encoded char array of hexadecimal characters to decode. Letters 187 * can be either uppercase or lowercase. 188 * @return the decoded data 189 * @throws IllegalArgumentException if the input is malformed 190 * 191 * @hide 192 */ 193 @SystemApi(client = MODULE_LIBRARIES) decode(char[] encoded)194 public static byte[] decode(char[] encoded) throws IllegalArgumentException { 195 return decode(encoded, false); 196 } 197 198 /** 199 * Decodes the provided hexadecimal sequence. 200 * 201 * @param encoded char array of hexadecimal characters to decode. Letters 202 * can be either uppercase or lowercase. 203 * @param allowSingleChar If {@code true} odd-length inputs are allowed and 204 * the first character is interpreted as the lower bits of the first 205 * result byte. If {@code false} odd-length inputs are not allowed. 206 * @return the decoded data 207 * @throws IllegalArgumentException if the input is malformed 208 * 209 * @hide 210 */ 211 @SystemApi(client = MODULE_LIBRARIES) decode(char[] encoded, boolean allowSingleChar)212 public static byte[] decode(char[] encoded, boolean allowSingleChar) 213 throws IllegalArgumentException { 214 int encodedLength = encoded.length; 215 int resultLengthBytes = (encodedLength + 1) / 2; 216 byte[] result = new byte[resultLengthBytes]; 217 218 int resultOffset = 0; 219 int i = 0; 220 if (allowSingleChar) { 221 if ((encodedLength % 2) != 0) { 222 // Odd number of digits -- the first digit is the lower 4 bits of the first result 223 // byte. 224 result[resultOffset++] = (byte) toDigit(encoded, i); 225 i++; 226 } 227 } else { 228 if ((encodedLength % 2) != 0) { 229 throw new IllegalArgumentException("Invalid input length: " + encodedLength); 230 } 231 } 232 233 for (; i < encodedLength; i += 2) { 234 result[resultOffset++] = (byte) ((toDigit(encoded, i) << 4) | toDigit(encoded, i + 1)); 235 } 236 237 return result; 238 } 239 toDigit(char[] str, int offset)240 private static int toDigit(char[] str, int offset) throws IllegalArgumentException { 241 // NOTE: that this isn't really a code point in the traditional sense, since we're 242 // just rejecting surrogate pairs outright. 243 int pseudoCodePoint = str[offset]; 244 245 if ('0' <= pseudoCodePoint && pseudoCodePoint <= '9') { 246 return pseudoCodePoint - '0'; 247 } else if ('a' <= pseudoCodePoint && pseudoCodePoint <= 'f') { 248 return 10 + (pseudoCodePoint - 'a'); 249 } else if ('A' <= pseudoCodePoint && pseudoCodePoint <= 'F') { 250 return 10 + (pseudoCodePoint - 'A'); 251 } 252 253 throw new IllegalArgumentException("Illegal char: " + str[offset] + " at offset " + offset); 254 } 255 } 256