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 /** 20 * Hexadecimal encoding where each byte is represented by two hexadecimal digits. 21 * @hide 22 */ 23 @libcore.api.CorePlatformApi 24 public class HexEncoding { 25 26 private static final char[] LOWER_CASE_DIGITS = { 27 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 28 }; 29 30 private static final char[] UPPER_CASE_DIGITS = { 31 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' 32 }; 33 34 /** Hidden constructor to prevent instantiation. */ HexEncoding()35 private HexEncoding() {} 36 37 /** 38 * Encodes the provided byte as a two-digit hexadecimal String value. 39 */ 40 @libcore.api.CorePlatformApi encodeToString(byte b, boolean upperCase)41 public static String encodeToString(byte b, boolean upperCase) { 42 char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS; 43 char[] buf = new char[2]; // We always want two digits. 44 buf[0] = digits[(b >> 4) & 0xf]; 45 buf[1] = digits[b & 0xf]; 46 return new String(buf, 0, 2); 47 } 48 49 /** 50 * Encodes the provided data as a sequence of hexadecimal characters. 51 */ 52 @libcore.api.CorePlatformApi encode(byte[] data)53 public static char[] encode(byte[] data) { 54 return encode(data, 0, data.length, true /* upperCase */); 55 } 56 57 /** 58 * Encodes the provided data as a sequence of hexadecimal characters. 59 */ 60 @libcore.api.CorePlatformApi encode(byte[] data, boolean upperCase)61 public static char[] encode(byte[] data, boolean upperCase) { 62 return encode(data, 0, data.length, upperCase); 63 } 64 65 /** 66 * Encodes the provided data as a sequence of hexadecimal characters. 67 */ 68 @libcore.api.CorePlatformApi encode(byte[] data, int offset, int len)69 public static char[] encode(byte[] data, int offset, int len) { 70 return encode(data, offset, len, true /* upperCase */); 71 } 72 73 /** 74 * Encodes the provided data as a sequence of hexadecimal characters. 75 */ encode(byte[] data, int offset, int len, boolean upperCase)76 private static char[] encode(byte[] data, int offset, int len, boolean upperCase) { 77 char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS; 78 char[] result = new char[len * 2]; 79 for (int i = 0; i < len; i++) { 80 byte b = data[offset + i]; 81 int resultIndex = 2 * i; 82 result[resultIndex] = (digits[(b >> 4) & 0x0f]); 83 result[resultIndex + 1] = (digits[b & 0x0f]); 84 } 85 86 return result; 87 } 88 89 /** 90 * Encodes the provided data as a sequence of hexadecimal characters. 91 */ 92 @libcore.api.CorePlatformApi encodeToString(byte[] data)93 public static String encodeToString(byte[] data) { 94 return encodeToString(data, true /* upperCase */); 95 } 96 97 /** 98 * Encodes the provided data as a sequence of hexadecimal characters. 99 */ 100 @libcore.api.CorePlatformApi encodeToString(byte[] data, boolean upperCase)101 public static String encodeToString(byte[] data, boolean upperCase) { 102 return new String(encode(data, upperCase)); 103 } 104 105 /** 106 * Decodes the provided hexadecimal string into a byte array. Odd-length inputs 107 * are not allowed. 108 * 109 * Throws an {@code IllegalArgumentException} if the input is malformed. 110 */ 111 @libcore.api.CorePlatformApi decode(String encoded)112 public static byte[] decode(String encoded) throws IllegalArgumentException { 113 return decode(encoded.toCharArray()); 114 } 115 116 /** 117 * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar} 118 * is {@code true} odd-length inputs are allowed and the first character is interpreted 119 * as the lower bits of the first result byte. 120 * 121 * Throws an {@code IllegalArgumentException} if the input is malformed. 122 */ 123 @libcore.api.CorePlatformApi decode(String encoded, boolean allowSingleChar)124 public static byte[] decode(String encoded, boolean allowSingleChar) 125 throws IllegalArgumentException { 126 return decode(encoded.toCharArray(), allowSingleChar); 127 } 128 129 /** 130 * Decodes the provided hexadecimal string into a byte array. Odd-length inputs 131 * are not allowed. 132 * 133 * Throws an {@code IllegalArgumentException} if the input is malformed. 134 */ 135 @libcore.api.CorePlatformApi decode(char[] encoded)136 public static byte[] decode(char[] encoded) throws IllegalArgumentException { 137 return decode(encoded, false); 138 } 139 140 /** 141 * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar} 142 * is {@code true} odd-length inputs are allowed and the first character is interpreted 143 * as the lower bits of the first result byte. 144 * 145 * Throws an {@code IllegalArgumentException} if the input is malformed. 146 */ 147 @libcore.api.CorePlatformApi decode(char[] encoded, boolean allowSingleChar)148 public static byte[] decode(char[] encoded, boolean allowSingleChar) 149 throws IllegalArgumentException { 150 int encodedLength = encoded.length; 151 int resultLengthBytes = (encodedLength + 1) / 2; 152 byte[] result = new byte[resultLengthBytes]; 153 154 int resultOffset = 0; 155 int i = 0; 156 if (allowSingleChar) { 157 if ((encodedLength % 2) != 0) { 158 // Odd number of digits -- the first digit is the lower 4 bits of the first result 159 // byte. 160 result[resultOffset++] = (byte) toDigit(encoded, i); 161 i++; 162 } 163 } else { 164 if ((encodedLength % 2) != 0) { 165 throw new IllegalArgumentException("Invalid input length: " + encodedLength); 166 } 167 } 168 169 for (; i < encodedLength; i += 2) { 170 result[resultOffset++] = (byte) ((toDigit(encoded, i) << 4) | toDigit(encoded, i + 1)); 171 } 172 173 return result; 174 } 175 toDigit(char[] str, int offset)176 private static int toDigit(char[] str, int offset) throws IllegalArgumentException { 177 // NOTE: that this isn't really a code point in the traditional sense, since we're 178 // just rejecting surrogate pairs outright. 179 int pseudoCodePoint = str[offset]; 180 181 if ('0' <= pseudoCodePoint && pseudoCodePoint <= '9') { 182 return pseudoCodePoint - '0'; 183 } else if ('a' <= pseudoCodePoint && pseudoCodePoint <= 'f') { 184 return 10 + (pseudoCodePoint - 'a'); 185 } else if ('A' <= pseudoCodePoint && pseudoCodePoint <= 'F') { 186 return 10 + (pseudoCodePoint - 'A'); 187 } 188 189 throw new IllegalArgumentException("Illegal char: " + str[offset] + " at offset " + offset); 190 } 191 } 192