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 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) 29 public class HexEncoding { 30 31 private static final char[] LOWER_CASE_DIGITS = { 32 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 33 }; 34 35 private static final char[] UPPER_CASE_DIGITS = { 36 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' 37 }; 38 39 /** Hidden constructor to prevent instantiation. */ HexEncoding()40 private HexEncoding() {} 41 42 /** 43 * Encodes the provided byte as a two-digit hexadecimal String value. 44 * 45 * @param b byte to encode 46 * @param upperCase {@code true} to use uppercase letters, {@code false} 47 * for lowercase 48 * @return the encoded string 49 * 50 * @hide 51 */ 52 @SystemApi(client = MODULE_LIBRARIES) 53 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) encodeToString(byte b, boolean upperCase)54 public static String encodeToString(byte b, boolean upperCase) { 55 char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS; 56 char[] buf = new char[2]; // We always want two digits. 57 buf[0] = digits[(b >> 4) & 0xf]; 58 buf[1] = digits[b & 0xf]; 59 return new String(buf, 0, 2); 60 } 61 62 /** 63 * Encodes the provided data as a sequence of hexadecimal characters. 64 * 65 * @param data byte array to encode 66 * @return the encoded data, using uppercase letters 67 * 68 * @hide 69 */ 70 @SystemApi(client = MODULE_LIBRARIES) 71 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) encode(byte[] data)72 public static char[] encode(byte[] data) { 73 return encode(data, 0, data.length, true /* upperCase */); 74 } 75 76 /** 77 * Encodes the provided data as a sequence of hexadecimal characters. 78 * 79 * @param data byte array to encode 80 * @param upperCase {@code true} to use uppercase letters, {@code false} 81 * for lowercase 82 * @return the encoded data 83 * 84 * @hide 85 */ 86 @SystemApi(client = MODULE_LIBRARIES) 87 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) encode(byte[] data, boolean upperCase)88 public static char[] encode(byte[] data, boolean upperCase) { 89 return encode(data, 0, data.length, upperCase); 90 } 91 92 /** 93 * Encodes the provided data as a sequence of hexadecimal characters. 94 * 95 * @param data byte array containing the data to encode 96 * @param offset offset of the data to encode in the {@code data} array 97 * @param len length of the data to encode in the {@code data} array 98 * @return the encoded data, using uppercase letters 99 * 100 * @hide 101 */ 102 @SystemApi(client = MODULE_LIBRARIES) 103 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) encode(byte[] data, int offset, int len)104 public static char[] encode(byte[] data, int offset, int len) { 105 return encode(data, offset, len, true /* upperCase */); 106 } 107 108 /** 109 * Encodes the provided data as a sequence of hexadecimal characters. 110 */ encode(byte[] data, int offset, int len, boolean upperCase)111 private static char[] encode(byte[] data, int offset, int len, boolean upperCase) { 112 char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS; 113 char[] result = new char[len * 2]; 114 for (int i = 0; i < len; i++) { 115 byte b = data[offset + i]; 116 int resultIndex = 2 * i; 117 result[resultIndex] = (digits[(b >> 4) & 0x0f]); 118 result[resultIndex + 1] = (digits[b & 0x0f]); 119 } 120 121 return result; 122 } 123 124 /** 125 * Encodes the provided data as a sequence of hexadecimal characters. 126 * 127 * @param data byte array to encode 128 * @return the encoded data, using uppercase letters 129 * 130 * @hide 131 */ 132 @SystemApi(client = MODULE_LIBRARIES) 133 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) encodeToString(byte[] data)134 public static String encodeToString(byte[] data) { 135 return encodeToString(data, true /* upperCase */); 136 } 137 138 /** 139 * Encodes the provided data as a sequence of hexadecimal characters. 140 * 141 * @param data byte array to encode. 142 * @param upperCase {@code true} to use uppercase letters, {@code false} 143 * for lowercase 144 * @return the encoded data 145 * 146 * @hide 147 */ 148 @SystemApi(client = MODULE_LIBRARIES) 149 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) encodeToString(byte[] data, boolean upperCase)150 public static String encodeToString(byte[] data, boolean upperCase) { 151 return new String(encode(data, upperCase)); 152 } 153 154 /** 155 * Decodes the provided hexadecimal sequence. Odd-length inputs are not 156 * allowed. 157 * 158 * @param encoded string of hexadecimal characters to decode. Letters 159 * can be either uppercase or lowercase. 160 * @return the decoded data 161 * @throws IllegalArgumentException if the input is malformed 162 * 163 * @hide 164 */ 165 @SystemApi(client = MODULE_LIBRARIES) 166 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) decode(String encoded)167 public static byte[] decode(String encoded) throws IllegalArgumentException { 168 return decode(encoded.toCharArray()); 169 } 170 171 /** 172 * Decodes the provided hexadecimal sequence. 173 * 174 * @param encoded string of hexadecimal characters to decode. Letters 175 * can be either uppercase or lowercase. 176 * @param allowSingleChar If {@code true} odd-length inputs are allowed and 177 * the first character is interpreted as the lower bits of the first 178 * result byte. If {@code false} odd-length inputs are not allowed. 179 * @return the decoded data 180 * @throws IllegalArgumentException if the input is malformed 181 * 182 * @hide 183 */ 184 @SystemApi(client = MODULE_LIBRARIES) 185 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) decode(String encoded, boolean allowSingleChar)186 public static byte[] decode(String encoded, boolean allowSingleChar) 187 throws IllegalArgumentException { 188 return decode(encoded.toCharArray(), allowSingleChar); 189 } 190 191 /** 192 * Decodes the provided hexadecimal sequence. Odd-length inputs are not 193 * allowed. 194 * 195 * @param encoded char array of hexadecimal characters to decode. Letters 196 * can be either uppercase or lowercase. 197 * @return the decoded data 198 * @throws IllegalArgumentException if the input is malformed 199 * 200 * @hide 201 */ 202 @SystemApi(client = MODULE_LIBRARIES) 203 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) decode(char[] encoded)204 public static byte[] decode(char[] encoded) throws IllegalArgumentException { 205 return decode(encoded, false); 206 } 207 208 /** 209 * Decodes the provided hexadecimal sequence. 210 * 211 * @param encoded char array of hexadecimal characters to decode. Letters 212 * can be either uppercase or lowercase. 213 * @param allowSingleChar If {@code true} odd-length inputs are allowed and 214 * the first character is interpreted as the lower bits of the first 215 * result byte. If {@code false} odd-length inputs are not allowed. 216 * @return the decoded data 217 * @throws IllegalArgumentException if the input is malformed 218 * 219 * @hide 220 */ 221 @SystemApi(client = MODULE_LIBRARIES) 222 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) decode(char[] encoded, boolean allowSingleChar)223 public static byte[] decode(char[] encoded, boolean allowSingleChar) 224 throws IllegalArgumentException { 225 int encodedLength = encoded.length; 226 int resultLengthBytes = (encodedLength + 1) / 2; 227 byte[] result = new byte[resultLengthBytes]; 228 229 int resultOffset = 0; 230 int i = 0; 231 if (allowSingleChar) { 232 if ((encodedLength % 2) != 0) { 233 // Odd number of digits -- the first digit is the lower 4 bits of the first result 234 // byte. 235 result[resultOffset++] = (byte) toDigit(encoded, i); 236 i++; 237 } 238 } else { 239 if ((encodedLength % 2) != 0) { 240 throw new IllegalArgumentException("Invalid input length: " + encodedLength); 241 } 242 } 243 244 for (; i < encodedLength; i += 2) { 245 result[resultOffset++] = (byte) ((toDigit(encoded, i) << 4) | toDigit(encoded, i + 1)); 246 } 247 248 return result; 249 } 250 toDigit(char[] str, int offset)251 private static int toDigit(char[] str, int offset) throws IllegalArgumentException { 252 // NOTE: that this isn't really a code point in the traditional sense, since we're 253 // just rejecting surrogate pairs outright. 254 int pseudoCodePoint = str[offset]; 255 256 if ('0' <= pseudoCodePoint && pseudoCodePoint <= '9') { 257 return pseudoCodePoint - '0'; 258 } else if ('a' <= pseudoCodePoint && pseudoCodePoint <= 'f') { 259 return 10 + (pseudoCodePoint - 'a'); 260 } else if ('A' <= pseudoCodePoint && pseudoCodePoint <= 'F') { 261 return 10 + (pseudoCodePoint - 'A'); 262 } 263 264 throw new IllegalArgumentException("Illegal char: " + str[offset] + " at offset " + offset); 265 } 266 } 267